(支付宝支付)Spring实现一个项目配置多个信息、付款给对应商户
如何实现一个项目配置多个商户信息付款给对应商户
最近在对接支付宝支付时,遇到了一个问题
用户在付款时,需要直接付款到指定支付宝账户。
这个需求也无可厚非,就像我们公司有四个分公司,分别在北京、上海等地,如果钱只能到总公司的账户上,那在财务结算的时候就很麻烦。
简单说一下问题背景:
公司准备上线一个商城应用,有分公司和服务商的概念,意思就是收钱的不是一个人。
前一阵子,蚂蚁金服投了一大笔钱,也算是达成了合作,所以在第三方支付中首选了支付宝,场景是手机网站支付,问题就在这出现了。目前支付宝只在当面付中支持配置多个收款账户,也是在社区里逛了一阵子才知道这个信息的。
蚂蚁金服开发者社区原贴:如何实现一个项目配置多个商户信息付款给对应商户
上面的帖子明确说明了目前实现的两种方式,很明显方案二看起来比较优雅,也比较好实现,但是很抱歉,手机网站支付不支持方案二。
ps:判断是否支持方案二的方法就是去看它的文档,如果请求参数中有 app_auth_token这个参数就是支持方案二的。
为了证实这个蛋疼的消息,我又专门发了一个帖子确认了一遍
手机网站支付遇到了一些问题
管理员明确的回复了我,只能用方案一,并且要申请 N 个应用,并全部上线。
Google 了一下,没搜到用这种方式实现的代码,只能自己写了。。
支付宝最终是根据请求过来的 appid 来判断哪一个商户收款(也就是请求是谁的 appid 就收款到谁的账号下)
所以我申请了四个应用。。。。
先定一个接口;
public interface AlipayClientConfig { /** * 网关. */ String getGatewayUrl(); /** * appid. */ String getAppId(); /** * 私钥. */ String getAppPrivateKey(); /** * 格式 默认json. */ String getFormat(); /** * 编码 默认utf-8. */ String getCharset(); /** * 支付宝公钥. */ String getAlipayPublicKey(); /** * 签名方式. */ String getSignType(); }
在定义一个抽象类实现接口;
@Getter @Setter public class AlipayAbstractClientConfig implements AlipayClientConfig { /** * 网关. */ private String gatewayUrl; /** * appid. */ private String appId; /** * 私钥. */ private String appPrivateKey; /** * 格式 默认json. */ private String format; /** * 编码 默认utf-8. */ private String charset; /** * 支付宝公钥. */ private String alipayPublicKey; /** * 签名方式. */ private String signType; }
然后是四个配置文件继承抽象类;
@Configuration(AlipayConstant.BEIJING_CLIENT) @ConfigurationProperties(prefix = "alipay.apenapi.beijing") @Getter @Setter public class AlipayBeiJingClientConfig extends AlipayAbstractClientConfig { } @Configuration(AlipayConstant.NANJING_CLIENT) @ConfigurationProperties(prefix = "alipay.apenapi.nanjing") @Getter @Setter public class AlipayNanJingClientConfig extends AlipayAbstractClientConfig { } @Configuration(AlipayConstant.SHANGHAI_CLIENT) @ConfigurationProperties(prefix = "alipay.apenapi.shanghai") @Getter @Setter public class AlipayShangHaiClientConfig extends AlipayAbstractClientConfig { } @Configuration(AlipayConstant.TIANJIN_CLIENT) @ConfigurationProperties(prefix = "alipay.apenapi.tianjin") @Getter @Setter public class AlipayTianJinClientConfig extends AlipayAbstractClientConfig { }注解的常量信息;
public interface AlipayConstant { String BEIJING_CLIENT = "BJ"; String TIANJIN_CLIENT = "TJ"; String NANJING_CLIENT = "NJ"; String SHANGHAI_CLIENT = "SH"; }
再贴一下配置文件信息;
alipay: apenapi: tianjin: gateway_url: https://openapi.alipay.com/gateway.do app_id: 2018********* app_private_key: KubiKtPbIoMLITLRMKTu9MwIwE8lC9Grg5 6c/00rsypY/b8SQh8ydYsEPt098JYAEhS6HvLQNz6W5XvCZNeAcv307kj3RYql2W Xntkh9Rr0s/cEc1WnHmBeUF3 alipay_public_key: MIIBIjANBgkqhkistdzwIDAQAB format: json charset: utf-8 sign_type: RSA2 beijing: gateway_url: https://openapi.alipay.com/gateway.do app_id: 2018********** app_private_key: JTj9Lz6G4W3RMO8QCCK8XYWdkEH5H5I alipay_public_key: MIIBIjANBgB format: json charset: utf-8 sign_type: RSA2 shanghai: gateway_url: https://openapi.alipay.com/gateway.do app_id: 2018******** app_private_key: MIIEvAIBADANBgcpUyx/WxcdhuAYmF3Di alipay_public_key: MIIBIjANBgkqhkiG9w0 format: json charset: utf-8 sign_type: RSA2 nanjing: gateway_url: https://openapi.alipay.com/gateway.do app_id: 20181******** app_private_key: MIIEvgIBA alipay_public_key: MIIBIjANBgk format: json charset: utf-8 sign_type: RSA2
在 service 层调用时,只要动态的获取不同的配置文件,即可初始化不同的 AlipayClient,向支付宝发起请求;
@Service @Slf4j @Transactional(readOnly = true) public class AlipayServiceImpl implements AlipayService { @Autowired private AlipayDetailConfig alipayDetailConfig; @Autowired private Map<String, AlipayClientConfig> configMap; @Override public AlipayTradeWapPayResponse wapPay(AlipayWayPayRequest request) throws AlipayApiException { String method = "wapPay"; log.debug("Enter {}", method); log.debug("WapPay form info : orderCode:【{}】 、orderTotalAmount:【{}】 " + "、orderSubject:【{}】 、orderDescription:【{}】 、quitUrl:【{}】 、companyCode:【{}】。", request.getOrderCode(), request.getOrderTotalAmount(), request.getOrderSubject(), request.getOrderDescription(), request.getQuitUrl(), request.getCompanyCode() ); AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest(); alipayRequest.setNotifyUrl(alipayDetailConfig.getNotifyUrl()); alipayRequest.setReturnUrl(alipayDetailConfig.getReturnUrl()); AlipayTradeWapPayModel payModel = new AlipayTradeWapPayModel(); //商户订单号 payModel.setOutTradeNo(request.getOrderCode()); //订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] payModel.setTotalAmount(request.getOrderTotalAmount().toString()); //订单标题 payModel.setSubject(request.getOrderSubject()); //订单描述 payModel.setBody(request.getOrderDescription()); //用户付款中途退出返回商户网站的地址 payModel.setQuitUrl(request.getQuitUrl()); //商品主类型 虚拟类商品不支持使用花呗渠道 payModel.setGoodsType(AlipayConstant.VIRTUAL_GOODS_TYPE); //该笔订单允许的最晚付款时间,逾期将关闭交易 payModel.setTimeoutExpress(alipayDetailConfig.getTimeoutExpress()); //销售产品码,与支付宝签约的产品码名称 payModel.setProductCode(AlipayConstant.WAP_PAY_PRODUCT_CODE); //设置回传参数 payModel.setPassbackParams(request.getCompanyCode()); alipayRequest.setBizModel(payModel); //返回支付信息 AlipayClient alipayClient = getAlipayClient(request.getCompanyCode()); log.debug("Exit {}", method); return alipayClient.pageExecute(alipayRequest); } /** * 根据分公司code返回对应的client. * * @param companyCode 分公司code. */ private AlipayClient getAlipayClient(String companyCode) { AlipayClientConfig config = configMap.get(companyCode); return new DefaultAlipayClient( config.getGatewayUrl(), config.getAppId(), config.getAppPrivateKey(), config.getFormat(), config.getCharset(), config.getAlipayPublicKey(), config.getSignType() ); }
至此,算是比较优雅的实现了付款给指定商户账号。
后记:微信支付早就解决了这个问题 ,也因此在社区里,一大批吐槽者疯狂吐槽支付宝的设计思路和实现方式。