手把手教你如何采用服务商模式实现微信支付

手把手教你如何采用服务商模式实现微信支付

文章目录

背景微信支付的模式一、前期准备1.注册服务商2.服务商入驻页面入驻申请证书重要参数说明

二、子商户支付1.下单流程图2.代码实现1.引入依赖2.支付配置3.相关配置类4.业务实现类5. 工具类

三、服务商进件1.特约商户进件流程、时序图2.代码实现

背景

小程序盛行时代,一般的企业中都都会包含多个小程序,而大部分的小程序通常都需要实现支付功能,本文将针对服务商模式进行微信支付进行详细讲解。

微信支付的模式

一般企业选用是服务商或者为渠道商模式,但是成为渠道商需要相关流量支撑才能申请,本文以服务商模式进行讲解。

咨询微信支付客服关于服务商的有些问题: 1.服务商和特约只能有一个,需要注销特约商户后申请成为服务商 2.服务商不能单独收款,只能给特约商户进行收款 3.服务商可以设置分账抽成微信会自动完成分账 4.服务商下特约商户收款会直接将钱打到特约商户下 5.直连或者特约可以成为注册另一个服务商下的特约(根据风险适时调整)

一、前期准备

1.注册服务商

服务商申请需要通过已做过公司认证的公众号,登录公司的微信服务号,在【微信支付】>【服务商申请】,直接跟着官方引导一步步操作即可,具体申请流程如下: 说明:所以企业需要申请公共号,才能申请注册服务商。

2.服务商入驻

通过服务商来开发的系统来帮助商户微信支付,首先需要完成商户号在服务商号中的入驻过程。服务商注册成功后,进入微信支付平台,登录服务商,进行商户入驻。一般商户入驻有两种,具体如下:

页面入驻调用API方式入驻

页面入驻

入驻可以看作是商户生成商户号的同时与服务商形成绑定关系。具体可以参考微信公众号中按流程指引一步步操作就行。

说明:商户入驻完成后,此商户才能用于微信支付。

申请证书

商户号入驻成功后,需要申请API证书。 说明:按照官方文档申请证书,设置密钥,设置好密钥后一定要在安全的前提下记住,之后只能重置不能查看。

重要参数说明

appid:服务商AppidmchId:服务商的商户idmchKey:证书的序列号subAppId:子商户小程序AppidsubOpenId:子商户小程序用户的openIdsubMchId:子商户的商户id

二、子商户支付

1.下单流程图

注意:下单流程和直连商户一样,但是接口和参数略有不同。直接参考合作伙伴平台Api

2.代码实现

服务商模式的微信支付的具体实现方案,本文采用的是Spring Boot集成weixin-java-pay来实现微信支付。 微信服务商JSAPI下单官方文档 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_1.shtml weixin-java-pay文档:http://binary.ac.cn/weixin-java-pay-javadoc/

踩坑:mchId和appid必须是服务商的而且是必填,如果服务商要给特约下单的appid是特约的appid也要写服务商的,subappid才可以写特约商户的。如果服务商给特约下答案appid是服务商的那么subappid可以不填。

1.引入依赖

com.github.binarywang

weixin-java-pay

4.5.0

2.支付配置

将证书放到resources下

# 微信支付配置

wx:

pay:

appId: wx1xxxx #服务商微信公众号或者小程序等的appid

subAppId: wxc0xxxxx #特约商户微信公众号或者小程序等的appid

mchId: 160000000 #服务商微信支付商户号

apiV3Key: 7xxxxxxxxxxxxxx #apiV3秘钥

certSerialNo: 2CCCCCCCCCCCA #证书号

privateKeyPath: classpath:cert/apiclient_key.pem #apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径

privateCertPath: classpath:cert/apiclient_cert.pem #apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径

notifyUrl: https://www.xxx.com/prod-api/anonymous/wx/notify/order #回调地址

3.相关配置类

说明:读取微信支付的配置信息

package com.ruoyi.xyhj.config;

import lombok.Data;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**

* wxpay pay properties.

*

* @author Binary Wang

*/

@Data

@ConfigurationProperties(prefix = "wx.pay")

public class WxPayProperties {

/**

* 设置微信公众号或者小程序等的appid

*/

private String appId;

/**

* 设置微信公众号或者小程序等的appid

*/

private String subAppId;

/**

* 微信支付商户号

*/

private String mchId;

/**

* 微信支付商户V3密钥

*/

private String apiV3Key;

/**

* 证书号

*/

private String certSerialNo;

/**

* apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径

*/

private String privateKeyPath;

/**

* apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径

*/

private String privateCertPath;

/**

* 回调地址

*/

private String notifyUrl;

}

说明:将微信支付的相关参数设置到wxjava中的cofig中。

package com.ruoyi.xyhj.config;

import com.github.binarywang.wxpay.config.WxPayConfig;

import com.github.binarywang.wxpay.service.WxPayService;

import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;

import lombok.AllArgsConstructor;

import org.apache.commons.lang3.StringUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;

import org.springframework.boot.context.properties.EnableConfigurationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

/**

* @author Binary Wang

*/

@Configuration

@ConditionalOnClass(WxPayService.class)

@EnableConfigurationProperties(WxPayProperties.class)

@AllArgsConstructor

public class WxPayConfiguration {

private WxPayProperties properties;

@Bean("wxPayService")

@ConditionalOnMissingBean

public WxPayService wxService() {

WxPayConfig payConfig = new WxPayConfig();

payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));

payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));

payConfig.setApiV3Key(StringUtils.trimToNull(this.properties.getApiV3Key()));

payConfig.setCertSerialNo(StringUtils.trimToNull(this.properties.getCertSerialNo()));

payConfig.setPrivateKeyPath(StringUtils.trimToNull(this.properties.getPrivateKeyPath()));

payConfig.setPrivateCertPath(StringUtils.trimToNull(this.properties.getPrivateCertPath()));

payConfig.setNotifyUrl(StringUtils.trimToNull(this.properties.getNotifyUrl()));

payConfig.setTradeType("JSAPI");

payConfig.setSignType("MD5");

WxPayService wxPayService = new WxPayServiceImpl();

wxPayService.setConfig(payConfig);

return wxPayService;

}

}

4.业务实现类

@RestController

@RequestMapping("/anonymous")

@Slf4j

public class TelAnonymousController

{

@Autowired

private WxPayService wxPayService;

//发起支付

@PostMapping("/wx/order/create")

public WxUnifiedOrderVo createOrder(){

//发起V3 服务商发起支付

EcommerceServiceImpl ecommerceService=new EcommerceServiceImpl(wxPayService);

// 1. 创建请求对象

PartnerTransactionsRequest orderRequest=new PartnerTransactionsRequest();

// 2. 根据订单系统传过来的订单信息组装支付参数,创建支付订单

//服务商应用ID

orderRequest.setSpAppid(wxPayProperties.getAppId());

//服务商户号

orderRequest.setSpMchid(wxPayProperties.getMchId());

//子商户号

orderRequest.setSubMchid(dealerMerchant.getMchid());

//子商户/二级商户应用ID

orderRequest.setSubAppid(wxPayProperties.getSubAppId());

//商品描述

orderRequest.setDescription("商品描述");

//商户订单号

orderRequest.setOutTradeNo("订单id");

//设置交易结束时间为24小时

orderRequest.setTimeExpire(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(new Date()));

//通知地址

orderRequest.setNotifyUrl(wxPayProperties.getNotifyUrl());

//订单金额

PartnerTransactionsRequest.Amount amount = new PartnerTransactionsRequest.Amount();

amount.setTotal(BaseWxPayRequest.yuanToFen("订单金额"));

orderRequest.setAmount(amount);

//支付者

PartnerTransactionsRequest.Payer payer = new PartnerTransactionsRequest.Payer();

payer.setSubOpenid("支付者的openid");

orderRequest.setPayer(payer);

//发起下单请求

TransactionsResult partner = ecommerceService.partner(TradeTypeEnum.JSAPI, partnerTransactionsRequest);

//生成签名

WxUnifiedOrderVo tokenJSAPI = WechatSignUtil.getTokenJSAPI(wxPayProperties.getSubAppId(), partner.getPrepayId(), wxPayProperties.getPrivateKeyPath());

tokenJSAPI.setOrderId(order.getId());

return tokenJSAPI;

}

//支付回调

@ApiOperation(value = "支付回调通知处理")

@PostMapping("/wx/notify/order")

public void parseOrderNotifyResult(@RequestBody String resultData) throws WxPayException {

log.info("回调:{}",resultData);

EcommerceServiceImpl ecommerceService = new EcommerceServiceImpl(wxPayService);

PartnerTransactionsNotifyResult notifyResult = ecommerceService.parsePartnerNotifyResult(resultData, null);

//此处解析到了回调信息

log.info("回调:{}",notifyResult.getResult());

//业务逻辑

}

//支付回调

@ApiOperation(value = "主动查询支付信息")

@PostMapping("/wx/order/select")

public void wxSelectOrderStatus() throws WxPayException {

//构建ecommerceService

EcommerceServiceImpl ecommerceService = new EcommerceServiceImpl(wxPayService);

//构建PartnerTransactionsQueryRequest对象

PartnerTransactionsQueryRequest queryRequest = new PartnerTransactionsQueryRequest();

queryRequest.setOutTradeNo("订单ID");

queryRequest.setSpMchid("服务商ID");

queryRequest.setSubMchid("特约商户ID");

PartnerTransactionsResult partnerTransactionsResult=new PartnerTransactionsResult();

try {

//普通查询订单API

partnerTransactionsResult = ecommerceService.queryPartnerTransactions(queryRequest);

} catch (WxPayException e) {

e.printStackTrace();

}

}

}

说明:实现微信创建订单、支付回调、查询订单接口。

5. 工具类

WxUnifiedOrderVo

import io.swagger.annotations.ApiModel;

import io.swagger.annotations.ApiModelProperty;

import lombok.Data;

@Data

@ApiModel

public class WxUnifiedOrderVo {

/**

* appid

*/

@ApiModelProperty("appId")

private String appId;

/**

* 时间戳

*/

@ApiModelProperty("时间戳")

private String timeStamp;

/**

* 随机字符串

*/

@ApiModelProperty("随机字符串")

private String nonceStr;

/**

* 小程序下单接口返回的prepay_id参数值,提交格式如:prepay_id=***

*/

@ApiModelProperty("小程序下单接口返回的prepay_id参数值,提交格式如:prepay_id=***")

private String packageStr;

/**

* 签名类型,默认为RSA,仅支持RSA。

*/

@ApiModelProperty("签名类型,默认为RSA,仅支持RSA。")

private String signType;

/**

* 签名

*/

@ApiModelProperty("签名")

private String paySign;

/**

* 订单id

*/

@ApiModelProperty("订单id")

private Long orderId;

}

WechatSignUtil

package com.ruoyi.xyhj.utils;

import com.ruoyi.xyhj.domain.vo.WxUnifiedOrderVo;

import java.io.IOException;

import java.io.InputStream;

import java.security.*;

import java.security.spec.InvalidKeySpecException;

import java.security.spec.PKCS8EncodedKeySpec;

import java.util.Base64;

import java.util.HashMap;

import java.util.UUID;

public class WechatSignUtil {

/**

* 参考网站 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml

* 计算签名值

*

* @param appId

* @param prepay_id

* @return

* @throws IOException

* @throws SignatureException

* @throws NoSuchAlgorithmException

* @throws InvalidKeyException

*/

public static WxUnifiedOrderVo getTokenJSAPI(String appId, String prepay_id, String privateKey) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {

// 获取随机字符串

String nonceStr = getNonceStr();

// 获取微信小程序支付package

String packagestr = "prepay_id=" + prepay_id;

long timestamp = System.currentTimeMillis() / 1000;

//签名,使用字段appId、timeStamp、nonceStr、package计算得出的签名值

String message = buildMessageTwo(appId, timestamp, nonceStr, packagestr);

//获取对应的签名

String signature = sign(message.getBytes("utf-8"),privateKey);

// 组装返回

WxUnifiedOrderVo vo = new WxUnifiedOrderVo();

vo.setAppId(appId);

vo.setTimeStamp(String.valueOf(timestamp));

vo.setNonceStr(nonceStr);

vo.setPackageStr(packagestr);

vo.setSignType("RSA");

vo.setPaySign(signature);

return vo;

}

/**

* 生成随机数

* @return

*/

public static String getNonceStr(){

return UUID.randomUUID().toString()

.replaceAll("-", "")

.substring(0, 32);

}

/**

* 拼接参数

*

* @return

*/

public static String buildMessageTwo(String appId, long timestamp, String nonceStr, String packag) {

return appId + "\n"

+ timestamp + "\n"

+ nonceStr + "\n"

+ packag + "\n";

}

/**

* 生成签名

*

* @return

*/

public static String sign(byte[] message,String privateKey) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {

Signature sign = Signature.getInstance("SHA256withRSA"); //SHA256withRSA

sign.initSign(getPrivateKey(privateKey));

sign.update(message);

return Base64.getEncoder().encodeToString(sign.sign());

}

/**

* 获取私钥

* @param filename 私钥文件路径 (required)

* @return 私钥对象

*/

public static PrivateKey getPrivateKey(String filename) throws IOException {

System.out.println("filename:" + filename);

filename = filename.replace("classpath:", "");

WechatSignUtil wechatSignUtil = new WechatSignUtil();

InputStream resourceAsStream = wechatSignUtil.getClass().getClassLoader().getResourceAsStream(filename);

byte[] bytes = new byte[0];

bytes = new byte[resourceAsStream.available()];

resourceAsStream.read(bytes);

String content = new String(bytes);

// String content = new String(Files.readAllBytes(Paths.get(resource.getPath())), "utf-8");

try {

String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")

.replace("-----END PRIVATE KEY-----", "")

.replaceAll("\\s+", "");

KeyFactory kf = KeyFactory.getInstance("RSA");

return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));

} catch (NoSuchAlgorithmException e) {

throw new RuntimeException("当前Java环境不支持RSA", e);

} catch (InvalidKeySpecException e) {

throw new RuntimeException("无效的密钥格式");

}

}

}

三、服务商进件

官方文档 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter11_1_1.shtml

1.特约商户进件流程、时序图

流程图 时序图 申请单状态如下

2.代码实现

@Operation(summary = "提交申请单")

@GetMapping("/createApply")

public R createApply(@RequestParam(required = false) String applymentId) throws WxPayException {

WxPayApplyment4SubCreateRequest request = new WxPayApplyment4SubCreateRequest();

// 主体资料:主体类型、是否是金融机构、营业执照、登记证书、组织机构代码证、单位证明函照片、经营者/法人身份证件、最终受益人信息列表(UBO)、小微辅助证明材料(subjectType为小微商户时必填)

WxPayApplyment4SubCreateRequest.SubjectInfo subjectInfo = WxPayApplyment4SubCreateRequest.SubjectInfo.builder().build()

.setFinanceInstitution(false)

.setBusinessLicenseInfo(null);// 省略.......

request.setSubjectInfo(subjectInfo);

// 补充材料

WxPayApplyment4SubCreateRequest.AdditionInfo additionInfo=new WxPayApplyment4SubCreateRequest.AdditionInfo();

additionInfo.setBusinessAdditionMsg("补充说明");

additionInfo.setBusinessAdditionPics(null) ;// 补充材料

additionInfo.setLegalPersonCommitment("法人开户承诺函");

additionInfo.setLegalPersonVideo("法人开户意愿视频");

request.setAdditionInfo(additionInfo);

// 结算银行账户

WxPayApplyment4SubCreateRequest.BankAccountInfo bankAccountInfo=new WxPayApplyment4SubCreateRequest.BankAccountInfo();

bankAccountInfo.setBankAccountType(BankAccountTypeEnum.BANK_ACCOUNT_TYPE_CORPORATE); // 账户类型:对公银行账户

bankAccountInfo.setAccountName("开户名称"); // 开户名称

bankAccountInfo.setAccountBank("开户银行");

bankAccountInfo.setBankAddressCode("开户银行省市编码");

bankAccountInfo.setBankBranchId("开户银行联行号");

bankAccountInfo.setBankName("开户银行全称(含支行)");

bankAccountInfo.setAccountNumber("银行账号");

request.setBankAccountInfo(bankAccountInfo);

// 业务申请编号

request.setBusinessCode("业务申请编号");

// 经营资料

request.setBusinessInfo(null); // 省略.......

// 超级管理员信息

request.setContactInfo(null);// 省略.......

// 结算规则

request.setSettlementInfo(null);// 省略.......

// 调用微信API

Applyment4SubService applyment4SubService=new Applyment4SubServiceImpl(wxPayService);

WxPayApplymentCreateResult apply = applyment4SubService.createApply(request);

String applyMentId = apply.getApplymentId(); // 返回申请单ID

return R.success(applyMentId);

}

@Operation(summary = "通过申请单号查询申请状态")

@GetMapping("/queryApply")

public R queryApply(@RequestParam(required = true) String applymentId) throws WxPayException {

// 调用API 查询申请状态

Applyment4SubService applyment4SubService=new Applyment4SubServiceImpl(wxPayService);

ApplymentStateQueryResult result = applyment4SubService.queryApplyStatusByApplymentId(applymentId);

return R.success(result);

}

相关推荐

神雕侠侣的历史背景是什么朝代揭秘
be365备用网址

神雕侠侣的历史背景是什么朝代揭秘

📅 08-01 👁️ 2580
第 1 部分:如何通过设置在Android上恢复出厂设置
如何选择适合你企业的网站建设公司排行榜?
手机摄像软件排行榜TOP10推荐
365bet手机客户端首页

手机摄像软件排行榜TOP10推荐

📅 08-09 👁️ 3392
闪租侠租机怎么样,对比阿里系租机就知道
电煅无烟煤
be365备用网址

电煅无烟煤

📅 07-30 👁️ 508