跳到主要内容

支付插件

支付插件体系说明

插件接口

PaymentPluginManager

插件方法描述

   /**
* 获取支付插件id,一般return实现类的bean
*
* @return
*/
String getPluginId();

/**
* 支付名称,如支付宝、微信
*
* @return
*/
String getPluginName();

/**
* 自定义客户端配置文件,所需要的配置
*
* @return
*/
List<ClientConfig> definitionClientConfig();

/**
* 支付,调起第三方支付,需要看第三方支付提供的接口情况调用
*
* @param bill
* @return
*/
Map pay(PayBill bill);

/**
* 支付完成同步回调
*
* @param tradeType
*/
void onReturn(TradeTypeEnum tradeType);

/**
* 支付完成异步回调
*
* @param clientType
* @return
*/
String onCallback(ClientType clientType);

/**
* 主动查询支付结果
*
* @param bill
* @return
*/
String onQuery(PayBill bill);


/**
* 退款,原路退回
*
* @param bill
* @return
*/
boolean onTradeRefund(RefundBill bill);

/**
* 查询退款状态
*
* @param bill
* @return
*/
String queryRefundStatus(RefundBill bill);

/**
* 是否支持原路退回 0 不支持 1支持
*
* @return
*/
Integer getIsRetrace();

/**
* 关闭交易
*
* @param outTradeNo 交易号
* @param configMap 支付配置
*/
void closeTrade(Map<String, String> configMap, String outTradeNo);

现有支付方式

中国银联、支付宝、微信、银联在线

开发新的支付插件

实现插件接口

@Service
public class YourPlugin implements PaymentPluginManager {
//其他方法略
}

注意:用@Service注解为spring bean,以便spring ioc识别,将插件扫描加入插件体系。

定义插件id及名字

    @Override
public String getPluginId() {
return "myPluginName";
}
@Override
public String getPluginName() {
return "xx支付";
}

注意:需要保持插件id的唯一性,这是插件机制调起插件依据

定义支付配置项

支付配置项的目的是定义用户使用支付插件前需要配置的一些支付参数,如商户秘钥等:

image-20201207115350453

要达到如上效果,首先需要理解 ClientConfig(客户端配置)和PayConfigItem这个两个类:

image-20201207120723846
public class ClientConfig implements Serializable{

@ApiModelProperty(name = "字段name")
private String key;

@ApiModelProperty(name = "字段文本提示")
private String name;

@ApiModelProperty(name = "字段文本提示",value = "config_list")
private List<PayConfigItem> configList;

@ApiModelProperty(name = "是否开启 1开启 0关闭",value = "is_open")
private Integer isOpen;
}

上述这个类在支付接口的 definitionClientConfig 方法中,我们假设需要定义PC端支付参数中的一个配置项“商户秘钥”:

    @Override
public List<ClientConfig> definitionClientConfig() {
//声明返回类型
List<ClientConfig> resultList = new ArrayList<>();
//声明客户端配置
ClientConfig config = new ClientConfig();
//声明配置文件映射实体
List<PayConfigItem> configList = new ArrayList<>();
//配置文件映射实体赋值
PayConfigItem item = new PayConfigItem();
item.setName("MerchantKey");
item.setText("商户秘钥");
configList.add(item);
//客户端配置赋值
config.setKey(ClientType.PC.getDbColumn());
config.setConfigList(configList);
config.setName("PC端");
resultList.add(config);
return resultList;
}

支付配置的获取

用户配置了支付的参数(如秘钥等)在支付调起时需要获取到这些参数,可以通过如下方法获取:

Map PaymentMethodManager.getConfig(clientType, pluginId);

入参:

  • clientType

    客户端类型,参见: cn.shoptnt.model.payment.enums.ClientType

  • pluginId

    支付插件id

####返回值

类型Map

ClientConfig 中包含 PayConfigItem 的集合 既 一个ClientConfig 对应多个 PayConfigItem ,PayConfigItem 一个对象就为一种支付方式的其中一个配置参数项。 例如:


微信配置的枚举
public enum WeixinConfigItem {

/**
* 商户号MCHID
*/
mchid("商户号MCHID"),
/**
* APPID
*/
appid("APPID"),
/**
* API密钥(key)
*/
key("API密钥(key)");

private String text;
...
}

@Override
public List<ClientConfig> definitionClientConfig() {
声明ClientConfig 集合,因为同一种支付 会有不同的 配置 例如 PC页面支付和 移动端支付
List<ClientConfig> resultList = new ArrayList<>();
声明一个 ClientConfig
ClientConfig config = new ClientConfig();
声明 PayConfigItem 集合
List<PayConfigItem> configList = new ArrayList<>();
通过遍历 枚举类 ,获得基本参数名和显示的内容
for (WeixinConfigItem configValue : WeixinConfigItem.values()) {
PayConfigItem item = new PayConfigItem();
item.setName(configValue.name());
item.setText(configValue.getText());
configList.add(item);
}
加入到 ClientConfig
resultList.add(config);
返回 List<ClientConfig>
return resultList;
}

编写支付逻辑

    @Override
public Map pay(PayBill bill) {
//在这里编写插件的支付逻辑
}

支付调起插件时,会调用此方法,入参为PayBill(支付账单参数),返回值为一个Map

入参

在订单创建时,系统会创建一个“支付账单”,支付体系面向的事“支付账单”而非“订单”,这里的PayBill就是“支付账单”,对应的是es_payment_bill表

  • 支付帐单表:es_payment_bill

    字段名类型与长度备注
    billIdbigint(20)主键
    bill_snvarchar(50)付款单号,提交给第三方平台单号
    sub_snbigint(20)子业务编号
    return_trade_novarchar(100)第三方平台返回交易号
    is_payint(1)是否已支付
    service_typevarchar(50)子业务类型
    payment_namevarchar(50)支付方式名称
    pay_configlongtext支付参数
    trade_pricedecimal(20,2)交易金额
    payment_plugin_iddecimal(20,2)支付插件

PayBill类说明:

public class PayBill {


/**
* 编号(交易或订单编号,或其它要扩展的其它类型编号)
*/
private String subSn;

/**
* 交易类型
*/
private TradeTypeEnum tradeType;

/**
* 账单编号,要传递给第三方平台的,不要管,系统自动生成
*/
private String billSn;

/**
* 要支付的金额
*/
private Double orderPrice;

/**
* normal:正常的网页跳转
* qr:二维码扫描
*/
private String payMode;




/**
* 客户端类型
*/
private ClientType clientType;

/**
* 支付插件
*/
private String pluginId;
}

这里值得关注的是:tradeType和subSn两个参数:

tradeType:交易类型,如order,trade等等要支付的订单类型

subSn: 订单或交易的单号

当支付成功时,系统会通过这两个参数来确定要更改状态的订单。

返回值

返回值为Map类型,这里需要把前端需要的参数通过压入map传递给前端,如:

        Map<String, String> result = new TreeMap<>();
params.put("gateway_url", getUrl());
return result;

回调地址的获取

第三方支付平台一般都需要在调用他的支付接口时传递回调地址和参数,在AbstractPaymentPlugin基类中内置了获取上述参数的方法:


/**
* 获取同步通知url
*/
protected String getReturnUrl(PayBill bill) {
//实现逻辑参考代码
}

/**
* 获取异步通知url

*/
protected String getCallBackUrl(TradeTypeEnum tradeType, ClientType clientType) {
//实现逻辑参考代码
}

支付回调方法的实现

    @Override
public void onReturn(TradeTypeEnum tradeType) {
//在这里实现支付同步回调的逻辑(如果平台需要)
}

@Override
public String onCallback( ClientType clientType) {
//在这里实现支付异步回调的逻辑
}

第三方平台支付成功后,一般都需要异步回调通知,那会通过插件机制调用到插件的onCallback方法,在这里需要做的操作一般为:参数验证签名、更改支付账单状态.

支付账单状态的改变

可以通过下面的方法更改支付账单的支付状态:

     * @param billSn 业务单号 (shoptnt生成)
* @param returnTradeNo 第三方平台回传单号(第三方平台的支付单号)
* @param payPrice 支付金额
void PaymentBillManager.paySuccess(billSn, returnTradeNo, payPrice);

billSn 为交易单号或者订单号,有shoptnt商城代码生成。
returnTradeNo 为调用的第三方支付平台单号。
payPrice 为在第三支付的金额。

订单状态的改变

shoptnt在内部处理了订单支付状态的变更,具体方式为:

原路退回逻辑的实现

需要实现下面三个方法:


@Override
public Integer getIsRetrace() {

}

@Override
public boolean onTradeRefund(RefundBill bill) {

}

@Override
public String queryRefundStatus(RefundBill bill) {

}