Braintree-国外支付对接(二)

在前文 国外支付对接:Braintree(一)的基础上   已经拿到了相关配置信息,接下来就是码代码了,这里完成的主要功能是支付与退款。

在此之前,先说一下Briantree的支付流程:

    第一步先生成clientToken,一组根据 MerchantId,BraintreePublicKey,BraintreePrivateKey生成的字符串,用于前端生成初始化支付控件。第二步点击支付按钮客户输入用户名密码确定支付之后,Briantree在前端会返回nonce给我们(相当于支付授权凭证)。第三步,将nonce传到后台,我们进行扣款。至此支付完成。

1.项目引用

  •    后端

 从官方下载的demo中可以看到,其实我们的后端需要用的dll就是一个:Braintree.dll,NUGET上也能下载

  

  •   前端

     需要的就是引用官方js,这个需要看个人需求吧,如果你不想麻烦自己写样式,可以直接使用官方的js生成的支付按钮,那么用drop-in UI即可。使用drop-in是最直接便利的方式,我们在前端直接引用:

<script src="https://js.braintreegateway.com/web/dropin/1.9.2/js/dropin.min.js"></script>

 生成的样式长这样:

     如果需要自己设计样式,按照自己的规则来控制前端的话,那就得使用Customer UI。那当然需要引用的js就不同了,前端的写法也就不同了,后面细说。这块主要的js是

<script src="https://js.braintreegateway.com/web/3.29.0/js/client.min.js"></script>

2.代码解析

  •  web.config配置

        API keys 拿到之后需要在程序中使用,我们直接配置在web.config中即可,当然安全着想也可以加密配置到数据库中。

       

  •    前端(这里先介绍使用drop-in UI的写法)

 html:
   form只需要2个参数amount,nonce。最重要的是要定义一个div控件给生成支付控件使用,这里使用的id为bt-dropin的div

<form id="payment-form" method="post" action="/checkouts/Create">
            <section>
                <label for="amount">
                    <span class="input-label">Amount</span>
                    <div class="input-wrapper amount-wrapper">
                        <input id="amount" name="amount" type="tel" min="0.01" placeholder="Amount" value="0.01">
                    </div>
                </label>

                <div class="bt-drop-in-wrapper">
                    <div id="bt-dropin"></div>
                </div>
            </section>
            <input id="nonce" name="payment_method_nonce" type="hidden" />
            <button class="button"  type="submit"><span>Test Transaction</span></button>
        </form>

js:

<script src="https://js.braintreegateway.com/web/dropin/1.9.2/js/dropin.min.js"></script>
<script>
    $(function () {
        var client_token = "@ViewBag.ClientToken";
        var form = document.querySelector('#payment-form');

        braintree.dropin.create({//支付控件初始化开始
            authorization: client_token,//由后端传过来的值,一组根据 MerchantId,BraintreePublicKey,BraintreePrivateKey生成的字符串
            container: '#bt-dropin',
            paypal: {
                flow: 'vault',
                buttonStyle: {  //可以修改一点点按钮的样式,限制性很多
                    color: 'black',
                    shape: 'rect',
                    size: 'medium'
                }
            },
            //此处与上面的paypal设置不一样,亲么可以自己去尝试一下,不同点在哪
            //paypal: {
            //    flow: 'checkout',
            //    amount: document.querySelector('#amount').value,
            //    currency: 'USD'
            //}, 
            card: {//此项选填,干掉也没关系
                cardholderName: { required: true }, //必填的话,就会多生成一个持卡人姓名的输入框
                overrides: {
                    fields: {
                        number: {
                            placeholder: 'Card Number',
                        },
                        cvv: {
                            placeholder: 'CVV'
                        },
                        postalCode: {
                            placeholder: 'Postal Code'
                        }
                    },
                }
            },
            //threeDSecure: {//3D安全校验,选填,用于信用卡支付的时候,若改卡的持卡人在开卡的时候启用了额外的身份校验,例如密码,那么点支付的时候则会弹出一个额外的框,输入密码。
            //    amount: document.querySelector('#amount').value
            //}
        }, function (createErr, instance) {
            form.addEventListener('submit', function (event) {
                event.preventDefault();
                instance.requestPaymentMethod(function (err, payload) {//客户输入密码等之后,接收返回的结果,即nonce,支付授权凭证
                    if (err) {
                        console.log('Error', err);
                        return;
                    }
                    // Add the nonce to the form and submit
                    document.querySelector('#nonce').value = payload.nonce;
                    form.submit();
                });
            });
        });
    });
</script>一组根据 MerchantId,BraintreePublicKey,BraintreePrivateKey生成的字符串
            container: '#bt-dropin',
            paypal: {
                flow: 'vault',
                buttonStyle: {  //可以修改一点点按钮的样式,限制性很多
                    color: 'black',
                    shape: 'rect',
                    size: 'medium'
                }
            },
            //此处与上面的paypal设置不一样,亲么可以自己去尝试一下,不同点在哪
            //paypal: {
            //    flow: 'checkout',
            //    amount: document.querySelector('#amount').value,
            //    currency: 'USD'
            //}, 
            card: {//此项选填,干掉也没关系
                cardholderName: { required: true }, //必填的话,就会多生成一个持卡人姓名的输入框
                overrides: {
                    fields: {
                        number: {
                            placeholder: 'Card Number',
                        },
                        cvv: {
                            placeholder: 'CVV'
                        },
                        postalCode: {
                            placeholder: 'Postal Code'
                        }
                    },
                }
            },
            //threeDSecure: {//3D安全校验,选填,用于信用卡支付的时候,若改卡的持卡人在开卡的时候启用了额外的身份校验,例如密码,那么点支付的时候则会弹出一个额外的框,输入密码。
            //    amount: document.querySelector('#amount').value
            //}
        }, function (createErr, instance) {
            form.addEventListener('submit', function (event) {
                event.preventDefault();
                instance.requestPaymentMethod(function (err, payload) {//客户输入密码等之后,接收返回的结果,即nonce,支付授权凭证
                    if (err) {
                        console.log('Error', err);
                        return;
                    }
                    // Add the nonce to the form and submit
                    document.querySelector('#nonce').value = payload.nonce;
                    form.submit();
                });
            });
        });
    });
</script>
  •  后端

      1.生成clientToken的规则有2种,根据需要来吧。

       由于braintree平台中虽然只有一个商户ID,即Merchant ID,但是确可以有多个Merchant Accounts,即收账账号,设置的界面:Account-->Merchant Account Info

 

  第一种,使用默认配置:

  每个Merchant ID都会有一个default Merchant Account,所以下面的写法,就是默认将款额收到默认账户上

var config = new BraintreeGateway(environment, merchantId, publicKey, privateKey) ;
var gateway = config.GetGateway();
var clientToken = gateway.ClientToken.generate();

  第二种:指另付款到某一个账号

var clientToken = gateway.ClientToken.generate(new ClientTokenRequest() { MerchantAccountId = "TestAccount" });

  2.综合

   支付功能:一共3个Action:

//生成clientToken 传到前端,用于生成支付控件
public ActionResult New()
      {        
            var gateway = config.GetGateway();
            //var clientToken = gateway.ClientToken.generate();
            var clientToken = gateway.ClientToken.generate(new ClientTokenRequest() { MerchantAccountId = "TestAccount" });
            ViewBag.ClientToken = clientToken;
            return View();
        }
//form提交,得到nonce之后,在这里进行扣款
 public ActionResult Create()
        {
            var gateway = config.GetGateway();
            Decimal amount;
            try
            {
                amount = Convert.ToDecimal(Request["amount"]);
            }
            catch (FormatException e)
            {
                TempData["Flash"] = "Error: 81503: Amount is an invalid format.";
                return RedirectToAction("New");
            }
            var nonce = Request["payment_method_nonce"];//得到前端传来的nonce参数
            var request = new TransactionRequest//新建交易请求
            {
                MerchantAccountId = "TestAccount",//注意这里,如果你的clientToken生成的时候设置了MerchantAccountId,那么扣款的时候也必须要加上这个参数,否则是会失败的
                Amount = amount,
                PaymentMethodNonce = nonce,
                Options = new TransactionOptionsRequest
                {
                    ThreeDSecure = new TransactionOptionsThreeDSecureRequest()//这里注意,如果你前端启用了3D安全,那么这里也需要启用
                    {
                        Required = true
                    },
                    SubmitForSettlement = true
                }
            };
            Result<Transaction> result = gateway.Transaction.Sale(request);//扣款
            if (result.IsSuccess())//成功
            {
                Transaction transaction = result.Target;
                //transaction.Id是官方生产的此交易的唯一编号,如果要进行查询和退款的话,就必须要将此ID记录数据库.
                return RedirectToAction("Show", new { id = transaction.Id });
            }
            else if (result.Transaction != null)
            {
                return RedirectToAction("Show", new { id = result.Transaction.Id, mesg = result.Message});
            }
            else
            {
                string errorMessages = "";
                foreach (ValidationError error in result.Errors.DeepAll())
                {
                    errorMessages += "Error: " + (int)error.Code + " - " + error.Message + "\n";
                }
                TempData["Flash"] = errorMessages;
                return RedirectToAction("New3");
            }
        }
//支付结果页展示
public ActionResult Show(String id, string mesg)
        {
            var gateway = config.GetGateway();
            Transaction transaction = gateway.Transaction.Find(id);
            if (transactionSuccessStatuses.Contains(transaction.Status))
            {
                //成功
            }
            else
            {
                //失败
            }
            ViewBag.Transaction = transaction;
            return View();
        }

 退款:

   这里要说明下,即时客户完成了交易,已经进行了扣款,但是如果要立马退款的话,是不行的。因为braintree内部也要进行交易审核,审核过程需要时间,而且是时间不固定,可能十几分钟,可能几个小时。所以这里我们要根据当前退款的订单状态进行是退款还是作废。2种操作的过程是不一样的。退款会在briantree账户上生成退款交易单,但是作废不会,虽然2种操作最都会退款给客户。

 public ActionResult RefundTest(string trId, decimal amount)
        {
            var gateway = config.GetGateway();
            try
            {
                Transaction transaction = gateway.Transaction.Find(trId);
                if (transaction.Status == TransactionStatus.SETTLED || transaction.Status == TransactionStatus.SETTLING)
                {//交易状态为以上时,方可进行退款操作
                    Result<Transaction> result = gateway.Transaction.Refund(trId, amount);
                    if (!result.IsSuccess())
                    {//退款失败
                        //Transaction transaction = result.Transaction;
                        //if (transaction.Status == TransactionStatus.SETTLEMENT_DECLINED)
                        //{
                        //    //Console.WriteLine(transaction.ProcessorSettlementResponseCode);
                        //    // e.g. "4001"
                        //    //Console.WriteLine(transaction.ProcessorSettlementResponseText);
                        //    // e.g. "Settlement Declined"
                        //}
                        return RedirectToAction("RefundResponce", new { msg = result.Message });
                    }
                    else
                    {
                        return RedirectToAction("RefundResponce", new { msg = "OK" });
                    }
                }
                else if (transaction.Status == TransactionStatus.AUTHORIZED || transaction.Status == TransactionStatus.SUBMITTED_FOR_SETTLEMENT ||
                    (transaction.PaymentInstrumentType == PaymentInstrumentType.PAYPAL_ACCOUNT && transaction.Status == TransactionStatus.SETTLEMENT_PENDING))
                {//交易状态为此状态时不可退款,但是能void交易,即作废,那么就可同时退款可客户
                    Result<Transaction> result = gateway.Transaction.Void(trId);
                    if (result.IsSuccess())
                    {
                        return RedirectToAction("RefundResponce", new { msg = "transaction successfully voided" });
                    }
                    else
                    {
                        return RedirectToAction("RefundResponce", new { msg = result.Message });
                        //foreach (ValidationError error in result.Errors.DeepAll())
                        //{
                        //    Console.WriteLine(error.Message);
                        //}
                    }
                }
            }catch(Exception ex)
            {
                return RedirectToAction("RefundResponce", new { msg = ex.Message });
            }
            return RedirectToAction("RefundResponce");
 }

//扣款结果显示
public ActionResult RefundResponce(string msg)
        {
            ViewBag.Mesg = msg;
            return View();
        }

至此支付和退款功能完成。

其实还有很多需要解说和注意的地方,还是自己去多多摸索的话学到的更多。虽然都是英文的,可以锻炼英文的说。

关于自定义支付控件样式,即Customer UI的使用,下篇谈,官方介绍,有demo,还可以自己编码测试的网站

https://developers.braintreepayments.com/guides/hosted-fields/examples/javascript/v3。

Braintree-国外支付对接(三) 之Customer UI

以上纯属个人独自研究成果,仅供参考,转载请注明出处

    

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值