在前文 国外支付对接 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 /script2.代码解析
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/Createsectionlabel for amountspan class input-label Amount /spandiv class input-wrapper amount-wrapperinput id amount name amount type tel min 0.01 placeholder Amount value 0.01/div/labeldiv class bt-drop-in-wrapperdiv id bt-dropin /div/div/sectioninput 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 /scriptscript$(function () { var client_token ViewBag.ClientTokenvar form document.querySelector( #payment-formbraintree.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: CVVpostalCode: { 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(); 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 Declinedreturn 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。
以上纯属个人独自研究成果 仅供参考 转载请注明出处