Spring Boot整合海外第三方支付Coda Payments
程序员文章站
2024-02-26 13:06:34
...
前言
由于公司项目需要对接海外支付,本次对接的是Coda Payments,该支付集成还需先开通账号才能看文档,比较麻烦。为方便大家后续集成,在此做个简单记录。
一、Coda Payment是什么?
新加坡在线支付平台Coda Payments。
官网:https://www.codapayments.com/
控制台:https://online.codapayments.com/merchant/home.action
二、使用步骤
1.配置yml
# Coda Payments
coda-payment:
api-key: <输入你的商户**>
# 根据集成文档上修改你要的国家代码,当前值为印度
country: 356
# 根据集成文档上修改你要的货币代码,当前值为印度
currency: 356
# 根据集成文档上修改你要的支付类型,当前值对应paytm-wallet
pay-type: 390
# 此处是沙箱环境的,生产环境需要替换为:https://airtime.codapayments.com/airtime/api/restful/v1.0/Payment
base-url: https://sandbox.codapayments.com/airtime/api/restful/v1.0/Payment
# 付款初始化URL(获取txtId)
payment-url: ${coda-payment.base-url}/init
# 查询支付结果URL
inquiry-payment-result-url: ${coda-payment.base-url}/inquiryPaymentResult
# APP端支付页面URL,生产环境需要替换为:https://airtime.codapayments.com/airtime/begin?type=3&txn_id=%d&browser_type=mobile-web
# 如果是Web端,则去除URL中的browser_type=mobile-web即可
front-end-payment-page-url: https://sandbox.codapayments.com/airtime/begin?type=3&txn_id=%d&browser_type=mobile-web
2.对应的Config
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* Coda Payment config
*
* @author Jiahai
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "coda-payment")
public class CodaPaymentConfig {
/**
* 商户key
*/
private String apiKey;
/**
* 国家编号
*/
private Short country;
private Short currency;
private Short payType;
/**
* 付款初始化URL(获取txtId)
*/
private String paymentUrl;
/**
* 查询支付结果URL
*/
private String inquiryPaymentResultUrl;
/**
* 前端支付页面URL
*/
private String frontEndPaymentPageUrl;
}
3.主要关注点:初始化交易ID、支付成功回调、根据交易ID查询订单支付情况。
(请求均为Post JSON)
例如,初始化交易ID
package com.wjh.codapayment.service;
import com.wjh.codapayment.config.CodaPaymentConfig;
import com.wjh.codapayment.request.*;
import com.wjh.codapayment.response.InitResultVO;
import com.wjh.codapayment.response.PaymentResultVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* CodaPayments 业务层
*
* @author Jiahai
*/
@Service
public class CodaPaymentsService {
/**
* 暂定:Coda Payments 充值"Q币"的itemInfo的code为 1
*/
private String itemInfoRechargeCode = "1";
/**
* 暂定:Coda Payments 充值"Q币"的itemInfo的name为 Q coin
*/
private String itemInfoRechargeName = "Q coin";
/**
* 固定值
*/
private String profileEntryMapKeyUserId = "user_id";
/**
* 暂定:profile entry中 need_mno_id 为 yes
*/
private final EntryMap NEED_MNO_ID_ENTRY_MAP = new EntryMap("need_mno_id", "yes");
@Autowired
private RestTemplate restTemplate;
@Autowired
private CodaPaymentConfig codaPaymentConfig;
/**
* 初始化交易,生成 txtId
*
* @param orderId 系统订单ID
* @return
*/
public Long init(Long orderId) {
InitRequest initRequest = this.initInitRequest(orderId);
List<ItemInfo> itemInfoList = new ArrayList<>();
itemInfoList.add(new ItemInfo("1", "itemInfoRechargeName", 169.00d, Short.parseShort("1")));
initRequest.setItems(itemInfoList)
.setProfile(new Profile()
.setEntry(Arrays.asList(new EntryMap(profileEntryMapKeyUserId, "1"), NEED_MNO_ID_ENTRY_MAP)));
InitRequestVO initRequestVO = new InitRequestVO(initRequest);
try {
InitResultVO initResultVO = restTemplate.postForObject(codaPaymentConfig.getPaymentUrl(), initRequestVO, InitResultVO.class);
if (initResultVO != null && initRequestVO.getInitRequest() != null && initResultVO.getInitResult().getResultCode() == 0) {
return initResultVO.getInitResult().getTxnId();
}
} catch (RestClientException e) {
System.out.println("失败");
e.printStackTrace();
}
throw new IllegalArgumentException("失败");
}
/**
* 用户充值-回调
*
* @param httpServletRequest
* @return
*/
public String callbackForRecharge(HttpServletRequest httpServletRequest) {
/**
* TxnId - 5995344541934063522
* OrderId - 1369124976459808
* TotalPrice - 169.00
* PaymentType - 390
* ResultCode - 0
* Checksum - 13facafd62479a8dcbb4a35e4d668657
*/
RechargeRequest rechargeRequest = new RechargeRequest()
.setTxnId(Long.valueOf(httpServletRequest.getParameter("TxnId")))
.setOrderId(httpServletRequest.getParameter("OrderId"))
.setResultCode(Integer.valueOf(httpServletRequest.getParameter("ResultCode")))
.setTotalPrice(Float.valueOf(httpServletRequest.getParameter("TotalPrice")))
.setPaymentType(Integer.valueOf(httpServletRequest.getParameter("PaymentType")))
.setChecksum(httpServletRequest.getParameter("Checksum"));
boolean flag = this.validate(rechargeRequest);
if (flag) {
// 校验通过
// 1、查询数据库订单记录
// -> 订单存在 && 支付平台是 Coda Payments
// -> 订单处于“待支付”?
// -> Coda Payments端 成功?(httpServletRequest中ResultCode为0则表示成功)
// 支付成功,需要返回下面字符串,Coda Payments规定0表示成功。可以自定义返回别的状态码用于表示失败。
return "ResultCode=0";
} else {
return "ResultCode=-1";
}
}
/**
* 根据txnId,查询支付信息
*
* @param txnId
*/
public void inquiryPaymentResult(Long txnId) {
// 初始化查询对象
InquiryPaymentRequest inquiryPaymentRequest = new InquiryPaymentRequest()
.setApikey(codaPaymentConfig.getApiKey())
.setTxnId(txnId);
InquiryPaymentRequestVO inquiryPaymentRequestVO = new InquiryPaymentRequestVO(inquiryPaymentRequest);
// 查询
try {
PaymentResultVO paymentResultVO = restTemplate.postForObject(codaPaymentConfig.getInquiryPaymentResultUrl(), inquiryPaymentRequestVO, PaymentResultVO.class);
if (paymentResultVO != null && paymentResultVO.getPaymentResult() != null) {
if (paymentResultVO.getPaymentResult().getResultCode() == 0) {
// 成功 TODO
} else {
// 失败 TODO
}
}
} catch (RestClientException e) {
System.out.println("失败");
e.printStackTrace();
}
}
/**
* 初始化InitRequest
*
* @param orderId 订单ID
* @return
*/
private InitRequest initInitRequest(Long orderId) {
return new InitRequest()
.setApiKey(codaPaymentConfig.getApiKey())
.setOrderId(orderId + "")
.setCountry(codaPaymentConfig.getCountry())
.setCurrency(codaPaymentConfig.getCurrency())
.setPayType(codaPaymentConfig.getPayType());
}
/**
* 回调校验
*
* @param rechargeRequest
* @return
*/
private boolean validate(RechargeRequest rechargeRequest) {
if (rechargeRequest == null) {
return false;
}
String values = rechargeRequest.getTxnId() + codaPaymentConfig.getApiKey() + rechargeRequest.getOrderId() + rechargeRequest.getResultCode();
// MD5 校验
return Objects.equals(rechargeRequest.getChecksum(), DigestUtils.md5DigestAsHex(values.getBytes()));
}
}
4、项目结构
request包与response包中的类较多,其实都是根据请求JSON而定制,相信大家自己看官方文档时就可轻松完成。
5、测试环境效果演示
总结
对接该支付,需要先有账号才可看文档,这个不像国内微信支付这么方便了。
还有,支付成功后,“成功页”或“失败页”需要自己写,比如success.html和fail.html。重定向URL需要“邮件”通知Coda Payments人员去配置!!
支付的回调URL也是需要“邮件”通知他们去配置!!
下一篇: Spring MVC(半注解模式)