在前文 国外支付对接:Braintree(一)的基础上 已经拿到了相关配置信息,接下来就是码代码了,这里完成的主要功能是支付与退款。
在此之前,先说一下Briantree的支付流程:
第一步先生成clientToken,一组根据 MerchantId,BraintreePublicKey,BraintreePrivateKey生成的字符串,用于前端生成初始化支付控件。第二步点击支付按钮客户输入用户名密码确定支付之后,Briantree在前端会返回nonce给我们(相当于支付授权凭证)。第三步,将nonce传到后台,我们进行扣款。至此支付完成。
1.项目引用
需要的就是引用官方js,这个需要看个人需求吧,如果你不想麻烦自己写样式,可以直接使用官方的js生成的支付按钮,那么用drop-in UI即可。使用drop-in是最直接便利的方式,我们在前端直接引用:
生成的样式长这样:
如果需要自己设计样式,按照自己的规则来控制前端的话,那就得使用Customer UI。那当然需要引用的js就不同了,前端的写法也就不同了,后面细说。这块主要的js是
2.代码解析
API keys 拿到之后需要在程序中使用,我们直接配置在web.config中即可,当然安全着想也可以加密配置到数据库中。
js:
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 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 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 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。
以上纯属个人独自研究成果,仅供参考,转载请注明出处