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

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使用watchdog实现文件资源监控

《python使用watchdog实现文件资源监控》watchdog支持跨平台文件资源监控,可以检测指定文件夹下文件及文件夹变动,下面我们来看看Python如何使用watchdog实现文件资源监控吧... python文件监控库watchdogs简介随着Python在各种应用领域中的广泛使用,其生态环境也

el-select下拉选择缓存的实现

《el-select下拉选择缓存的实现》本文主要介绍了在使用el-select实现下拉选择缓存时遇到的问题及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录项目场景:问题描述解决方案:项目场景:从左侧列表中选取字段填入右侧下拉多选框,用户可以对右侧

Python pyinstaller实现图形化打包工具

《Pythonpyinstaller实现图形化打包工具》:本文主要介绍一个使用PythonPYQT5制作的关于pyinstaller打包工具,代替传统的cmd黑窗口模式打包页面,实现更快捷方便的... 目录1.简介2.运行效果3.相关源码1.简介一个使用python PYQT5制作的关于pyinstall

使用Python实现大文件切片上传及断点续传的方法

《使用Python实现大文件切片上传及断点续传的方法》本文介绍了使用Python实现大文件切片上传及断点续传的方法,包括功能模块划分(获取上传文件接口状态、临时文件夹状态信息、切片上传、切片合并)、整... 目录概要整体架构流程技术细节获取上传文件状态接口获取临时文件夹状态信息接口切片上传功能文件合并功能小

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur

C#实现文件读写到SQLite数据库

《C#实现文件读写到SQLite数据库》这篇文章主要为大家详细介绍了使用C#将文件读写到SQLite数据库的几种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录1. 使用 BLOB 存储文件2. 存储文件路径3. 分块存储文件《文件读写到SQLite数据库China编程的方法》博客中,介绍了文

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

JAVA利用顺序表实现“杨辉三角”的思路及代码示例

《JAVA利用顺序表实现“杨辉三角”的思路及代码示例》杨辉三角形是中国古代数学的杰出研究成果之一,是我国北宋数学家贾宪于1050年首先发现并使用的,:本文主要介绍JAVA利用顺序表实现杨辉三角的思... 目录一:“杨辉三角”题目链接二:题解代码:三:题解思路:总结一:“杨辉三角”题目链接题目链接:点击这里

基于Python实现PDF动画翻页效果的阅读器

《基于Python实现PDF动画翻页效果的阅读器》在这篇博客中,我们将深入分析一个基于wxPython实现的PDF阅读器程序,该程序支持加载PDF文件并显示页面内容,同时支持页面切换动画效果,文中有详... 目录全部代码代码结构初始化 UI 界面加载 PDF 文件显示 PDF 页面页面切换动画运行效果总结主

SpringBoot实现基于URL和IP的访问频率限制

《SpringBoot实现基于URL和IP的访问频率限制》在现代Web应用中,接口被恶意刷新或暴力请求是一种常见的攻击手段,为了保护系统资源,需要对接口的访问频率进行限制,下面我们就来看看如何使用... 目录1. 引言2. 项目依赖3. 配置 Redis4. 创建拦截器5. 注册拦截器6. 创建控制器8.