braintree支付开发
braintree介绍
流程介绍
-
前端从服务端请求一个客户端令牌,并初始化客户端SDK。
-
服务端SDK生成客户端令牌并将其发送回客户端
-
客户提交付款信息,客户端SDK将该信息传递给Braintree,并返回付款方式随机数
-
前端付款方式随机数发送给服务端
-
服务端收到付款方式随机数后,使用SDK向Braintree发起交易请求。
环境配置
注册Braintree帐号
-
前往braintree开发者文档注册沙盒帐号
-
登录后点击齿轮⚙==>API==>Keys==>APIKeys==>PrivateKey==>View 备用
BraintreeGateway gateway = new BraintreeGateway( Environment.SANDBOX, "f6tw2cm2c24yfbs7", "ds8szsps6fc9tjky", "bf4a3e7971a8e4be2dada2f7ca19573a" );
注册Paypal帐号
-
前往paypal开发者文档注册沙盒帐号
-
登录沙盒控制台后点击 My Apps & Credentials 选择 SandBox
-
创建app ---------- REST API apps下点击Create app
- 输入 App name
- Sandbox Business Account 选择一个business.example.com帐号
-
获取app参数备用--------点击进入app
#### Sandbox account #### luozong@business.example.com #### Client ID ##### ATN8f09gmB9Njm0iuoyApCV04GXbhbfltRzQPMHtjEL3i2oIZO0ShB31nIczY_hK1W0bVbYRUQaKIer6 #### Secret #### EGQzXV9F46OEBFBZHNkk04CAIXRwZW1r4K6X40anp2RKL4dYa4q-11qLxcuW1fyHZ-DA01Y9Oa_xC86j
设置braintree链接到paypal
-
前往braintree开发者文档登录braintree沙盒控制台
-
登录后点击齿轮==>Processing==>Processing Options==>Payment Methods==>Paypal==>Link Sandbox
-
进入到PayPal Sandbox Credentials ,填写以下信息
####PayPal Email##### luozong@business.example.com ####PayPal Client Id#### ATN8f09gmB9Njm0iuoyApCV04GXbhbfltRzQPMHtjEL3i2oIZO0ShB31nIczY_hK1W0bVbYRUQaKIer6 ####PayPal Client Secret#### EGQzXV9F46OEBFBZHNkk04CAIXRwZW1r4K6X40anp2RKL4dYa4q
点击 Link Paypal Sandbox 按钮
直接支付
公司的流程和代码不符合,看看流程就好
前端代码
该例子创建了 信用卡支付、paypal支付
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<!--jquery为了调用服务端获取token-->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.2.1/jquery.min.js" crossorigin="anonymous"></script>
<!--braintree出信用卡要加-->
<script src="https://js.braintreegateway.com/web/dropin/1.23.0/js/dropin.min.js"></script>
<!--出paypal按钮要加-->
<script src="https://www.paypal.com/sdk/js?client-id=sb"></script><!--沙盒client-id=sb,pro client-id取paypal的clientID-->
<script src="https://js.braintreegateway.com/web/3.64.2/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.64.2/js/paypal-checkout.min.js"></script>
<script>
$(function(){
//获取tonken
var token = null;
$.ajax({
url:'/braintree/getToken',
type:'post',
success:function(data){
token = data;
},
async:false
});
//渲染信用卡交易按钮
var button = document.querySelector('#submit-button');
//信用卡输入显示
braintree.dropin.create({
authorization: token,
container: '#dropin-container'
}, function (createErr, instance) {
button.addEventListener('click', function () {
instance.requestPaymentMethod(function (err, payload) {
// Submit payload.nonce to your server
//发送payload.nonce以及订单信息到服务端
});
});
});
// 创建paypal按钮代码
braintree.client.create({
authorization: token
}, function (clientErr, clientInstance) {
// Stop if there was a problem creating the client.
// This could happen if there is a network error or if the authorization
// is invalid.
if (clientErr) {
console.error('Error creating client:', clientErr);
return;
}
// Create a PayPal Checkout component.
braintree.paypalCheckout.create({
client: clientInstance
}, function (paypalCheckoutErr, paypalCheckoutInstance) {
if(paypalCheckoutErr){
console.error('Error creating paypalCheckoutInstance:', paypalCheckoutErr);
}
paypalCheckoutInstance.loadPayPalSDK({
currency: 'USD',
intent: 'capture'
}, function () {
paypal.Buttons({
fundingSource: paypal.FUNDING.PAYPAL,
createOrder: function () {
//此处可以去服务端下订单,获取订单具体信息在放到createPayment里面
return paypalCheckoutInstance.createPayment({
flow: 'checkout', // Required
amount: 10.00, // Required
currency: 'USD', // Required, must match the currency passed in with loadPayPalSDK
intent: 'capture', // Must match the intent passed in with loadPayPalSDK
//地址信息
enableShippingAddress: true,
shippingAddressEditable: false,
shippingAddressOverride: {
recipientName: 'Scruff McGruff',
line1: '1234 Main St.',
line2: 'Unit 1',
city: 'Chicago',
countryCode: 'US',
postalCode: '60652',
state: 'IL',
phone: '123.456.7890'
}
});
},
onApprove: function (data, actions) {
return paypalCheckoutInstance.tokenizePayment(data, function (err, payload) {
// Submit `payload.nonce` to your server
//发送payload.nonce以及订单信息到服务端请求进行一次付款交易(transaction)
});
},
onCancel: function (data) {
console.log('PayPal payment cancelled', JSON.stringify(data, 0, 2));
},
onError: function (err) {
console.error('PayPal error', err);
}
}).render('#paypal-button').then(function () {
// The PayPal button will be rendered in an html element with the ID
// `paypal-button`. This function will be called when the PayPal button
// is set up and ready to be used
});
});
});
});
});
</script>
</head>
<body>
<div id="dropin-container"></div>
<button id="submit-button">Request payment method</button>
<div id="paypal-button"></div>
</body>
</html>
后端代码
//简单的gatway配置,来自braintree帐号
public class GatewayFactory {
public static BraintreeGateway getlisiGateway() {
BraintreeGateway gateway = new BraintreeGateway(Environment.SANDBOX,
"f6tw2cm2c24yfbs7", "ds8szsps6fc9tjky", "bf4a3e7971a8e4be2dada2f7ca19573a");
return gateway;
}
}
@RestController
@RequestMapping("/braintree")
public class BraintreeController {
private static BraintreeGateway gateway = GatewayFactory.getlisiGateway();
@PostMapping("/getToken")
public String getToken(){
//官方写new ClientTokenRequest().customerId("customerId")可以不要。
ClientTokenRequest clientTokenRequest = new ClientTokenRequest();
String clientToken = gateway.clientToken().generate(clientTokenRequest);
return clientToken;
}
//从前端获取的交易信息不只有nonce,还有金额,设备等,orderId也可以传
@PostMapping("/pay")
public Msg pay(String nonce) {
TransactionRequest request = new TransactionRequest()
.amount(new BigDecimal("10.00"))
.paymentMethodNonce(nonce)
.deviceData("h5")
.options()
.submitForSettlement(true)
.done();
Result<Transaction> result = gateway.transaction().sale(request);
if(result.isSuccess()){
return Msg.ok(result.getTarget().getStatus().toString());
}else{
return Msg.fail(result.getMessage());
}
}
}
坑点
- 官方写new ClientTokenRequest().customerId(“customerId”)可以不要。
- Transaction状态会自动转,算是官方的审核时间,自动的。
- 信用卡 submit_for_settlted 后续过30分钟自动转为 settlted
- paypal settlting 后续过30分钟自动转为 settlted
订阅支付
公司的流程和代码不符合,看看流程就好
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.2.1/jquery.min.js" crossorigin="anonymous"></script>
<!--信用卡支付-->
<script src="https://js.braintreegateway.com/web/dropin/1.23.0/js/dropin.min.js"></script>
<!--paypal支付-->
<script src="https://js.braintreegateway.com/web/3.64.2/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.64.2/js/paypal-checkout.min.js"></script>
<script src="https://www.paypal.com/sdk/js?client-id=sb&vault=true"></script>
<script>
$(function(){
var token = null;
$.ajax({
url:'/braintree/getToken?'+new Date().getTime(),
type:'post',
success:function(data){
token = data;
},
async:false
});
alert(token);
var button = document.querySelector('#submit-button');
braintree.dropin.create({
authorization: token,
container: '#dropin-container'
}, function (createErr, instance) {
button.addEventListener('click', function () {
instance.requestPaymentMethod(function (err, payload) {
// Submit payload.nonce to your server
if(confirm("是否继续支付0.01USD?")){
$.ajax({
url:'/braintree/pay?'+new Date().getTime(),
type:'post',
data:'nonce='+payload.nonce,
success:function(data){
alert(data.msg);
}
});
}
});
});
});
// Create a client.
braintree.client.create({
authorization: token
}, function (clientErr, clientInstance) {
alert(clientInstance);
// Stop if there was a problem creating the client.
// This could happen if there is a network error or if the authorization
// is invalid.
if (clientErr) {
console.error('Error creating client:', clientErr);
return;
}
// Create a PayPal Checkout component.
braintree.paypalCheckout.create({
client: clientInstance
}, function (paypalCheckoutErr, paypalCheckoutInstance) {
if(paypalCheckoutErr){
console.error('Error creating paypalCheckoutInstance:', paypalCheckoutErr);
}
paypalCheckoutInstance.loadPayPalSDK({
currency: 'USD',
intent: 'capture',
vault: true
}, function () {
paypal.Buttons({
fundingSource: paypal.FUNDING.PAYPAL,
createBillingAgreement: function () {
return paypalCheckoutInstance.createPayment({
flow: 'vault' // Required
});
},
onApprove: function (data, actions) {
return paypalCheckoutInstance.tokenizePayment(data, function (err, payload) {
// Submit `payload.nonce` to your server
debugger;
if(confirm("是否继续支付10.00USD?")){
$.ajax({
url:'/braintree/pay2?'+new Date().getTime(),
type:'post',
data:'nonce='+payload.nonce,
success:function(data){
alert(data.msg);
}
});
}
});
},
onCancel: function (data) {
console.log('PayPal payment cancelled', JSON.stringify(data, 0, 2));
},
onError: function (err) {
console.error('PayPal error', err);
}
}).render('#paypal-button').then(function () {
// The PayPal button will be rendered in an html element with the ID
// `paypal-button`. This function will be called when the PayPal button
// is set up and ready to be used
});
});
});
});
});
</script>
</head>
<body>
<div id="dropin-container"></div>
<div id="paypal-button"></div>
<button id="submit-button">Request payment method</button>
</body>
</html>
后端代码
@RestController
@RequestMapping("/braintree")
public class BraintreeController {
private static BraintreeGateway gateway = GatewayFactory.getlisiGateway();
@PostMapping("/getToken")
public String getToken(){
ClientTokenRequest clientTokenRequest = new ClientTokenRequest();
String clientToken = gateway.clientToken().generate(clientTokenRequest);
return clientToken;
}
@PostMapping("/pay2")
public Msg pay2(String nonce) {
//保存用户
CustomerRequest customerRequest = new CustomerRequest()
.id(UUID.randomUUID().toString())
.paymentMethodNonce(nonce)
.firstName("Fred")
.lastName("Jones")
.creditCard().done();
Result<Customer> customerResult = gateway.customer().create(customerRequest);
Customer customer = gateway.customer().find(customerResult.getTarget().getId());
String token = customer.getDefaultPaymentMethod().getToken();
SubscriptionRequest subscriptionRequest = new SubscriptionRequest().planId("jhk2").paymentMethodToken(token);
Result<Subscription> result = gateway.subscription().create(subscriptionRequest);
if(result.isSuccess()){
return Msg.ok(result.getTarget().getStatus().toString());
}else{
return Msg.fail(result.getMessage());
}
}
}
坑点
-
订阅如果一直被declined 2046,表示帐号有问题,需要重新注册。我郁闷了好几天,最后重新注册就好了。