本文主要是介绍手把手教你如何采用服务商模式实现微信支付,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 背景
- 微信支付的模式
- 一、前期准备
- 1.注册服务商
- 2.服务商入驻
- 页面入驻
- 申请证书
- 重要参数说明
- 二、子商户支付
- 1.下单流程图
- 2.代码实现
- 1.引入依赖
- 2.支付配置
- 3.相关配置类
- 4.业务实现类
- 5. 工具类
- 三、服务商进件
- 1.特约商户进件流程、时序图
- 2.代码实现
背景
小程序盛行时代,一般的企业中都都会包含多个小程序,而大部分的小程序通常都需要实现支付功能,本文将针对服务商模式进行微信支付进行详细讲解。
微信支付的模式
一般企业选用是服务商或者为渠道商模式,但是成为渠道商需要相关流量支撑才能申请,本文以服务商模式进行讲解。
咨询微信支付客服关于服务商的有些问题:
1.服务商和特约只能有一个,需要注销特约商户后申请成为服务商
2.服务商不能单独收款,只能给特约商户进行收款
3.服务商可以设置分账抽成微信会自动完成分账
4.服务商下特约商户收款会直接将钱打到特约商户下
5.直连或者特约可以成为注册另一个服务商下的特约(根据风险适时调整)
一、前期准备
1.注册服务商
服务商申请需要通过已做过公司认证的公众号,登录公司的微信服务号,在【微信支付】>【服务商申请】,直接跟着官方引导一步步操作即可,具体申请流程如下:
说明:所以企业需要申请公共号,才能申请注册服务商。
2.服务商入驻
通过服务商来开发的系统来帮助商户微信支付,首先需要完成商户号在服务商号中的入驻过程。服务商注册成功后,进入微信支付平台,登录服务商,进行商户入驻。一般商户入驻有两种,具体如下:
- 页面入驻
- 调用API方式入驻
页面入驻
入驻可以看作是商户生成商户号的同时与服务商形成绑定关系。具体可以参考微信公众号中按流程指引一步步操作就行。
说明:商户入驻完成后,此商户才能用于微信支付。
申请证书
商户号入驻成功后,需要申请API证书。
说明:按照官方文档申请证书,设置密钥,设置好密钥后一定要在安全的前提下记住,之后只能重置不能查看。
重要参数说明
- appid:服务商Appid
- mchId:服务商的商户id
- mchKey:证书的序列号
- subAppId:子商户小程序Appid
- subOpenId:子商户小程序用户的openId
- subMchId:子商户的商户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.引入依赖
<dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-pay</artifactId><version>4.5.0</version>
</dependency>
2.支付配置
将证书放到resources下
# 微信支付配置
wx:pay:appId: wx1xxxx #服务商微信公众号或者小程序等的appidsubAppId: wxc0xxxxx #特约商户微信公众号或者小程序等的appidmchId: 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")@ConditionalOnMissingBeanpublic 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
{@Autowiredprivate WxPayService wxPayService; //发起支付@PostMapping("/wx/order/create")public WxUnifiedOrderVo createOrder(){//发起V3 服务商发起支付EcommerceServiceImpl ecommerceService=new EcommerceServiceImpl(wxPayService);// 1. 创建请求对象PartnerTransactionsRequest orderRequest=new PartnerTransactionsRequest();// 2. 根据订单系统传过来的订单信息组装支付参数,创建支付订单//服务商应用IDorderRequest.setSpAppid(wxPayProperties.getAppId());//服务商户号orderRequest.setSpMchid(wxPayProperties.getMchId());//子商户号orderRequest.setSubMchid(dealerMerchant.getMchid());//子商户/二级商户应用IDorderRequest.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 {//构建ecommerceServiceEcommerceServiceImpl ecommerceService = new EcommerceServiceImpl(wxPayService);//构建PartnerTransactionsQueryRequest对象PartnerTransactionsQueryRequest queryRequest = new PartnerTransactionsQueryRequest();queryRequest.setOutTradeNo("订单ID");queryRequest.setSpMchid("服务商ID");queryRequest.setSubMchid("特约商户ID");PartnerTransactionsResult partnerTransactionsResult=new PartnerTransactionsResult();try {//普通查询订单APIpartnerTransactionsResult = 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();// 获取微信小程序支付packageString 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"); //SHA256withRSAsign.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<String> 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);// 省略.......// 调用微信APIApplyment4SubService applyment4SubService=new Applyment4SubServiceImpl(wxPayService);WxPayApplymentCreateResult apply = applyment4SubService.createApply(request);String applyMentId = apply.getApplymentId(); // 返回申请单IDreturn R.success(applyMentId);}@Operation(summary = "通过申请单号查询申请状态")@GetMapping("/queryApply")public R<ApplymentStateQueryResult> queryApply(@RequestParam(required = true) String applymentId) throws WxPayException {// 调用API 查询申请状态Applyment4SubService applyment4SubService=new Applyment4SubServiceImpl(wxPayService);ApplymentStateQueryResult result = applyment4SubService.queryApplyStatusByApplymentId(applymentId);return R.success(result);}
这篇关于手把手教你如何采用服务商模式实现微信支付的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!