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

2024-03-03 14:40

本文主要是介绍手把手教你如何采用服务商模式实现微信支付,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 背景
  • 微信支付的模式
  • 一、前期准备
    • 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);}

这篇关于手把手教你如何采用服务商模式实现微信支付的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/769934

相关文章

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

java父子线程之间实现共享传递数据

《java父子线程之间实现共享传递数据》本文介绍了Java中父子线程间共享传递数据的几种方法,包括ThreadLocal变量、并发集合和内存队列或消息队列,并提醒注意并发安全问题... 目录通过 ThreadLocal 变量共享数据通过并发集合共享数据通过内存队列或消息队列共享数据注意并发安全问题总结在 J

SpringBoot+MyBatis-Flex配置ProxySQL的实现步骤

《SpringBoot+MyBatis-Flex配置ProxySQL的实现步骤》本文主要介绍了SpringBoot+MyBatis-Flex配置ProxySQL的实现步骤,文中通过示例代码介绍的非常详... 目录 目标 步骤 1:确保 ProxySQL 和 mysql 主从同步已正确配置ProxySQL 的

JS 实现复制到剪贴板的几种方式小结

《JS实现复制到剪贴板的几种方式小结》本文主要介绍了JS实现复制到剪贴板的几种方式小结,包括ClipboardAPI和document.execCommand这两种方法,具有一定的参考价值,感兴趣的... 目录一、Clipboard API相关属性方法二、document.execCommand优点:缺点:

nginx部署https网站的实现步骤(亲测)

《nginx部署https网站的实现步骤(亲测)》本文详细介绍了使用Nginx在保持与http服务兼容的情况下部署HTTPS,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值... 目录步骤 1:安装 Nginx步骤 2:获取 SSL 证书步骤 3:手动配置 Nginx步骤 4:测