tech_banner
paypal braintree支付对接_野蜂的博客-CSDN博客

paypal本身有sdk,不过这里选择使用Braintree服务进行对接 paypal本身也比较推荐这种方式。

准备工作

paypal账号

braintree账号(包括正式账号和沙盒账号)

申请流程这里不做说明了。

配置过程 登陆paypal开发后台 点击右上角的Dashboard 左边菜单栏中找到Sandbox– Account 在这里可以创建测试账号 创建账号时注意选择类型personal 账号密码注意修改 密码修改后看不到。左边菜单栏中找到Dashboard– My Apps Credentials 可以看到Sandbox和Live两个选项 分别时沙盒和正式环境的app配置。两边配置的步骤是一样的 选择Create App 填写相关内容完成创建。点击刚刚创建的app可以看到Sandbox account、Client ID、Secret 等下需要用到。往下滑 可以看到Add Webhook按钮 点击创建 这里需要准备webhook的接收地址 注意必须是https地址。使用braintree沙盒账号登陆braintree沙盒后台 点击右上角齿轮 选择Processing进入 下方Payment Methods中找到paypal并开启 点击Options 配置PayPal Email PayPal Client Id PayPal Client Secret。相关参数由第2步获取。(正式环境配置过程类似 需要使用正式账号登陆barintree正式环境后台)braintree后台首页点击右上角齿轮 选择Fraud Management进入 可以设置相关信用卡支付安全校验 相关配置说明可以查看braintree 部分配置需要谨慎 可能会导致用户信用卡无法使用。braintree后台首页点击右上角齿轮 选择API 可以看到Keys、Webhooks、Security 其中Keys中的API Keys下可以点击Private Key的VIEW拿到Merchant ID、Public Key、Private Key 是请求braintree api时所必须的东西。Webhooks中需要配置对应的webhook地址 接受消息通知 配置以后可以使用Check URL进行地址校验。Security可以开启IP和主机名限制。额外需要注意的一点 braintree后台首页点击右上角齿轮 选择Business 在Merchant Accounts中可以看到Merchant Account 如果有多个 使用braintree时需要进行指定 另外注意每个Merchant Account对应的Currency 即货币 如果使用时 用到了不对应的货币 将无法成功完成支付流程。配置Plan(如果有订阅订单 会用到) braintree后台首页顶部选择Subscriptions 跳转后选择Plans 点击New Plan 进行Plan创建 填写相关内容 其中Plan ID是调用api时所需参数 如果需要添加试用期 选中Trial Period 请求api时需要开启试用。 开发流程 引入braintree的jar包(maven项目) 开发参考braintree开发文档 dependencygroupId com.braintreepayments.gateway /groupIdartifactId braintree-java /artifactIdversion 2.87.0 /version/dependency 获取braintree的token 交给客户端 由客户端braintreeSDK换取paymentMethodNonce 如果使用了braintree的订阅服务 还需要客户端获取用户账户的firstName和lastName 服务端代码如下 public String getBraintreeToken() { try { BraintreeGateway gateway new BraintreeGateway( Environment.SANDBOX, PayConfig.BraintreeMerchantId, PayConfig.BraintreePublicKey, PayConfig.BraintreePrivateKey return gateway.clientToken().generate(); } catch (Exception e) { e.printStackTrace(); return null; 客户端获取到相关参数后 服务端通过api 进行订单创建、结算。

(1). 普通订单

public void braintreeTransaction(String nonce) { try { BraintreeGateway gateway new BraintreeGateway( Environment.SANDBOX, PayConfig.BraintreeMerchantId, PayConfig.BraintreePublicKey, PayConfig.BraintreePrivateKey TransactionRequest request new TransactionRequest() .amount(new BigDecimal(100)) //客户端根据服务器返回的accesstoken获取的paymentMethodNonce .paymentMethodNonce(nonce) //映射到PayPal的发票号码 .orderId(yourServerOrderId) .descriptor() //描述符显示在客户CC报表中。22个字符马克斯// .name( Descriptor displayed in customer CC statements. 22 char max ) .done() .shippingAddress() //对应商家账号的firstName .firstName(PayConfig.BraintreeFirstName) //对应商家账号的lastName .lastName(PayConfig.BraintreeLastName) //公司名// .company( Braintree )// .streetAddress( 1 E 1st St )// .extendedAddress( Suite 403 )// .locality( Bartlett )// .region( IL )// .postalCode( 60103 )// .countryCodeAlpha2( US ) .done() .options() //是否结算。如果不结算 用户的金额不会被扣除 需要手工去后台确认收款 .submitForSettlement(true) .paypal() //PayPal自定义字段 .customField( PayPal custom field ) //PayPal电子邮件说明 .description( ) .done() //If you want to create a new payment method in the Vault upon a successful transaction, use the this .storeInVaultOnSuccess(true) .done(); Result Transaction saleResult gateway.transaction().sale(request); if (saleResult.isSuccess()) { Transaction transaction saleResult.getTarget(); transactionId transaction.getId(); System.out.println( Success ID: transaction.getId()); System.out.println( transaction JSON.toJSON(transaction)); } else { //支付失败的情况 logger.error( Message: {} ,saleResult.getMessage()); logger.error( Error: {} ,saleResult.getErrors().toString()); logger.error( Error-JSON: {} ,JSON.toJSON(saleResult.getErrors())); } catch (Exception e) { e.printStackTrace();

(2). 订阅订单 需要注意braintree本身允许用户重复订阅 相关逻辑需要自行实现

public void braintreeSubscription(String nonce,String firstName,String lastName) { try { BraintreeGateway gateway new BraintreeGateway( Environment.SANDBOX, PayConfig.BraintreeMerchantId, PayConfig.BraintreePublicKey, PayConfig.BraintreePrivateKey CustomerRequest customerRequest new CustomerRequest() .firstName(firstName) .lastName(lastName) .paymentMethodNonce(nonce); Result Customer customerResult gateway.customer().create(customerRequest); //顾客信息不必每次获取 可以将顾客id或者token缓存起来下次使用 但需要注意用户本次使用的paypal账户是否与服务器缓存的一致 以防扣错账户。 Customer customer customerResult.getTarget(); if (customer null){ logger.error( 获取用户买家信息失败 ); return; SubscriptionRequest request new SubscriptionRequest() .paymentMethodToken(customer.getPaymentMethods().get(0).getToken()) .planId(yourPlanId) //启用试用期 启用以后 不能设置开始时间 会冲突// .trialPeriod(isFreeFirst) .options() //设置立即开始 如果启用了试用期 就不能设置本项 .startImmediately(true) .paypal() .description( ) .done() .done(); Result Subscription subscriptionResult gateway.subscription().create(request); try { System.out.println(JSON.toJSON(subscriptionResult)); } catch (Exception e) { e.printStackTrace(); if (subscriptionResult.isSuccess()) { orderSuccess true; Subscription subscription subscriptionResult.getTarget(); List Transaction transactionList subscription.getTransactions(); String subscriptionId subscription.getId(); } else { //支付失败的情况 logger.error( Message: {} ,subscriptionResult.getMessage()); logger.error( Error: {} ,subscriptionResult.getErrors().toString()); logger.error( Error-JSON: {} ,JSON.toJSON(subscriptionResult.getErrors())); } catch (Exception e) { e.printStackTrace(); 普通transaction会通过paypal的webhook进行通知。subscription会分别通过paypal和braintree进行webhook通知。

(1). paypal的webhook可以参考WebhooksManagementAPI文档

public String paypalNotify( RequestBody(required false) byte[] body, HttpServletRequest request, HttpServletResponse response) { PrintWriter out null; JSONObject rsJson new JSONObject(); try { String bodyStr null; JSONObject paramJson null; try { bodyStr new String(body, utf-8 ); paramJson JSONObject.parseObject(bodyStr); } catch (Exception e) { e.printStackTrace(); * PAYMENT.SALE.COMPLETED if (paramJson! null){ String eventType paramJson.getString( event_type ); if ( PAYMENT.SALE.COMPLETED .equalsIgnoreCase(eventType)){ //支付完成处理逻辑 JSONObject resourceJson paramJson.getJSONObject( resource ); String orderNo resourceJson.getString( invoice_number ); String paymentId resourceJson.getString( parent_payment ); out response.getWriter(); rsJson.put( status , 200 ); } catch (Exception e) { e.printStackTrace(); rsJson.put( status , 500 ); out.println(rsJson.toString()); out.flush(); out.close(); return null;

(2). braintree的Webhook回调处理 配置webhook时可以使用Check URL进行测试 braintree的webhook回调携带参数为bt_signature、bt_payload 可以参考braintreeWebhook文档

public String braintreeNotify(HttpServletRequest request, HttpServletResponse response)PrintWriter out null; JSONObject rsJson new JSONObject(); try { String signature null; String payload null; Object btSignatureObj request.getParameter( bt_signature ); Object btPayloadObj request.getParameter( bt_payload ); if (btSignatureObj! null){ signature btSignatureObj.toString(); if (btPayloadObj! null){ payload btPayloadObj.toString(); CErrorData cErrorData null; if (!CStr.isEmpty(signature) !CStr.isEmpty(payload)){ BraintreeGateway gateway new BraintreeGateway( Environment.SANDBOX, PayConfig.BraintreeMerchantId, PayConfig.BraintreePublicKey, PayConfig.BraintreePrivateKey WebhookNotification webhookNotification gateway.webhookNotification().parse(signature,payload); if ( CHECK .equals(webhookNotification.getKind().name())){ //测试 return; Subscription subscription webhookNotification.getSubscription(); if (subscription null){ //没有订阅信息 return; //如果是试用订阅 则不包含transactions List Transaction transactionList subscription.getTransactions(); if ( SUBSCRIPTION_WENT_ACTIVE .equals(webhookNotification.getKind().name())) { //创建订阅的第一个授权交易 或者成功的交易将订阅从\" 过期”状态转移到\" 活动”状态。具有试用期的订阅从试用期进入第一个计费周期后不会触发此通知。 }else if ( SUBSCRIPTION_CHARGED_SUCCESSFULLY .equals(webhookNotification.getKind().name())){ //订阅成功进入下一个计费周期 即续订成功 out response.getWriter(); rsJson.put( status , 200 ); } catch (Exception e) { rsJson.put( status , 500 ); e.printStackTrace(); out.println(rsJson.toString()); out.flush(); out.close(); return null;