文章目录
背景
小程序盛行时代,一般的企业中都都会包含多个小程序,而大部分的小程序通常都需要实现支付功能,本文将针对服务商模式进行微信支付进行详细讲解。
微信支付的模式
一般企业选用是服务商或者为渠道商模式,但是成为渠道商需要相关流量支撑才能申请,本文以服务商模式进行讲解。
咨询微信支付客服关于服务商的有些问题:
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 #服务商微信公众号或者小程序等的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<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);// 省略.......
// 调用微信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<ApplymentStateQueryResult> queryApply(@RequestParam(required = true) String applymentId) throws WxPayException {
// 调用API 查询申请状态
Applyment4SubService applyment4SubService=new Applyment4SubServiceImpl(wxPayService);
ApplymentStateQueryResult result = applyment4SubService.queryApplyStatusByApplymentId(applymentId);
return R.success(result);
}
今天的文章微信支付服务商怎么赚钱_支付服务商「建议收藏」分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/85104.html