微信支付之IJpay使用

标签: 无 分类: 未分类 创建时间:2022-11-10 05:41:56 更新时间:2024-11-23 10:24:25

前言

使用IJpay进行支付接口的开发,主要包括下面几个步骤,这里主要是使用的是 API V2 版本的接口进行的调试。

参考文章:
1.若依分离版整合IJpay实现第三方支付-(微信支付篇_v2)
2.IJPay 入门 这是教程,我这里摘录对自己有用的地方,WxPayApiConfigKit.setThreadLocalWxPayApiConfig(apiConfig); 设置微信支付配置

1.引入依赖

主要通过pom引入依赖

1
2
3
4
5
6
<!--支付依赖-->
<dependency>
<groupId>com.github.javen205</groupId>
<artifactId>IJPay-All</artifactId>
<version>2.9.4</version>
</dependency>

2.编写配置

主要appId、appSecret还有证书路径等

1
2
3
4
5
6
7
8
9
10
11
12
13
wxpay:
## 应用编号
appId:
## appSecret 是 appId 对应的接口密码,微信公众号授权获取用户 openId 时使用
appSecret:
## 微信支付商户号
mchId:
## APIv2 密钥
partnerKey:
## apiclient_cert.p12 证书路径,在微信商户后台下载
certPath:
## 外网访问项目的域名,支付通知中会使用
domain:

其中的秘钥和证书,需要到微信支付->商户平台->API安全进行配置,这里需要配置一个证书的路径。可以配置系统的绝对路径,但是程序使用的是docker部署,那么这个路径该如何配置呢?

解决方法就是使用目录映射的方法,比如我在docker-compose.yml中,使用volumes进行了目录映射

参考文章:
1.什么是商户API证书?如何获取商户API证书?
2.证书相关 推荐使用微信支付提供的SDK。
3.获取微信支付相关配置 certPath: apiclient_cert.p12 证书绝对路径,在微信商户后台下载

3.编写配置类

有了配置文件,需要进行读取配置文件内容,生成一个bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
@Component
@ConfigurationProperties(prefix = "wxpay")
public class WxPayProperties {
private String appId;
private String appSecret;
private String mchId;
private String partnerKey;
private String certPath;
private String domain;

public String getAppId() {
return appId;
}

public void setAppId(String appId) {
this.appId = appId;
}

public String getAppSecret() {
return appSecret;
}

public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}

public String getMchId() {
return mchId;
}

public void setMchId(String mchId) {
this.mchId = mchId;
}

public String getPartnerKey() {
return partnerKey;
}

public void setPartnerKey(String partnerKey) {
this.partnerKey = partnerKey;
}

public String getCertPath() {
return certPath;
}

public void setCertPath(String certPath) {
this.certPath = certPath;
}

public String getDomain() {
return domain;
}

public void setDomain(String domain) {
this.domain = domain;
}

@Override
public String toString() {
return "WxPayBean [appId=" + appId + ", appSecret=" + appSecret + ", mchId=" + mchId + ", partnerKey="
+ partnerKey + ", certPath=" + certPath + ", domain=" + domain + "]";
}
}

4.编写接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
@RestController
@RequestMapping("/wxPay")
public class PayController {
private final static Logger logger= LoggerFactory.getLogger(PayController.class);

@Autowired
private WxPayProperties wxPayBean; // 微信配置

private String notifyUrl;
private String refundNotifyUrl;
private static final String USER_PAYING = "USERPAYING";

/**
* 获取微信配置
* @return
*/
public WxPayApiConfig getApiConfig() {
WxPayApiConfig apiConfig;
try {
apiConfig = WxPayApiConfigKit.getApiConfig(wxPayBean.getAppId());
} catch (Exception e) {
apiConfig = WxPayApiConfig.builder()
.appId(wxPayBean.getAppId())
.mchId(wxPayBean.getMchId())
.partnerKey(wxPayBean.getPartnerKey())
.certPath(wxPayBean.getCertPath())
.domain(wxPayBean.getDomain())
.build();
// 设置默认配置
WxPayApiConfigKit.setThreadLocalWxPayApiConfig(apiConfig);
}
// 微信支付回调地址
notifyUrl = apiConfig.getDomain().concat("/wxPay/payNotify");
// 微信退款回调地址
refundNotifyUrl = apiConfig.getDomain().concat("/wxPay/refundNotify");
return apiConfig;
}

/**
* 公众号支付,代码调用成功后,会返回一个支付id,前端可以通过这个在公众号上唤起微信支付。
*/
@RequestMapping(value = "/webPay", method = {RequestMethod.POST, RequestMethod.GET})
public AjaxResult webPay(HttpServletRequest request, @RequestParam("total_fee") String totalFee) {
// openId,采用 网页授权获取 access_token API:SnsAccessTokenApi获取
String openId = (String) request.getSession().getAttribute("openId");
if (openId == null) {
openId = "11111111";
}

if (StrUtil.isEmpty(openId)) {
return AjaxResult.error("openId is null");
}
if (StrUtil.isEmpty(totalFee)) {
return AjaxResult.error("请输入数字金额");
}
String ip = IpKit.getRealIp(request);
if (StrUtil.isEmpty(ip)) {
ip = "127.0.0.1";
}
// 获取微信支付配置
WxPayApiConfig wxPayApiConfig = getApiConfig();
// 创建统一接口
Map<String, String> params = UnifiedOrderModel
.builder()
.appid(wxPayApiConfig.getAppId())
.mch_id(wxPayApiConfig.getMchId())
.nonce_str(WxPayKit.generateStr())
.body("IJPay 让支付触手可及-公众号支付")
.attach("Node.js 版:https://gitee.com/javen205/TNWX")
.out_trade_no(WxPayKit.generateStr())
.total_fee("1000")
.spbill_create_ip(ip)
.notify_url(notifyUrl)
.trade_type(TradeType.JSAPI.getTradeType())
.openid(openId)
.build()
.createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
// 执行微信支付
String xmlResult = WxPayApi.pushOrder(false, params);
logger.info(xmlResult);
// 处理支付返回结果
Map<String, String> resultMap = WxPayKit.xmlToMap(xmlResult);
String returnCode = resultMap.get("return_code");
String returnMsg = resultMap.get("return_msg");
if (!WxPayKit.codeIsOk(returnCode)) {
return AjaxResult.error(returnMsg);
}
String resultCode = resultMap.get("result_code");
if (!WxPayKit.codeIsOk(resultCode)) {
return AjaxResult.error(returnMsg);
}

// 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
String prepayId = resultMap.get("prepay_id");
// 进行二次签名封装唤起支付参数
Map<String, String> packageParams = WxPayKit.prepayIdCreateSign(prepayId, wxPayApiConfig.getAppId(),
wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

// 返回信息
return AjaxResult.success(packageParams);
}

/**
* 退款通知
*/
@RequestMapping(value = "/refundNotify", method = {RequestMethod.POST, RequestMethod.GET})
public String refundNotify(HttpServletRequest request) {
String xmlMsg = HttpKit.readData(request);
logger.info("退款通知=" + xmlMsg);
Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);

String returnCode = params.get("return_code");
// 注意重复通知的情况,同一订单号可能收到多次通知,请注意一定先判断订单状态
if (WxPayKit.codeIsOk(returnCode)) {
String reqInfo = params.get("req_info");
String decryptData = WxPayKit.decryptData(reqInfo, WxPayApiConfigKit.getWxPayApiConfig().getPartnerKey());
logger.info("退款通知解密后的数据=" + decryptData);
// 更新订单信息
// 发送通知等
Map<String, String> xml = new HashMap<String, String>(2);
xml.put("return_code", "SUCCESS");
xml.put("return_msg", "OK");
return WxPayKit.toXml(xml);
}
return null;
}

/**
* 异步通知
* 这里面可以写判断支付成功之后的及具体业务需求
*/
@RequestMapping(value = "/payNotify", method = {RequestMethod.POST, RequestMethod.GET})
public String payNotify(HttpServletRequest request) {
String xmlMsg = HttpKit.readData(request);
logger.info("支付通知=" + xmlMsg);
Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);

String returnCode = params.get("return_code");

// 注意重复通知的情况,同一订单号可能收到多次通知,请注意一定先判断订单状态
// 注意此处签名方式需与统一下单的签名类型一致
if (WxPayKit.verifyNotify(params, WxPayApiConfigKit.getWxPayApiConfig().getPartnerKey(), SignType.HMACSHA256)) {
if (WxPayKit.codeIsOk(returnCode)) {
// 更新订单信息
// 发送通知等
Map<String, String> xml = new HashMap<String, String>(2);
xml.put("return_code", "SUCCESS");
xml.put("return_msg", "OK");
return WxPayKit.toXml(xml);
}
}
return null;
}
}
参考文章:
1.微信支付 API v3 使用v2接口还是v3接口,这两种方式是不一样的。
2.统一下单 这是微信统一下单接口,无论是微信小程序,Native支付,还是其他的支付方式,都需要使用这个后端接口实现统一下单。body:商品简单描述;attach:附加数据,在查询 API 和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据;
3.Native支付接入前准备 这是Native支付模式,API v3版本的接口说明
4.Native支付 这个也是微信支付的开发文档,不过这里是API v2版本的。商户支付回调URL设置指引:进入商户平台–>产品中心–>开发配置,进行配置和修改,如图6.6所示。

问题

(1) 无法将输入源“/body/xml/total_fee”映射到目标字段“标价金额”中,此字段需要一个合法的 64 位有符号整数

【解决方法】
这个total_fee的单位是分,不能包括小数点。


(2) 签名错误,请检查后再试
这个问题我以为是因为使用了错误的证书引起的,后来我专门重新申请了一个证书,最后还是不行。

【解决方法】
这个错误的原因很多,我的问题主要就是我使用了 API v2 的支付,但是我的partnerKey填入了 API v3 的秘钥。

(4) 此商家的收款功能已被限制,暂无法支付。商家可以登录微信商户平台/微信支付商家助手小程序查看原因和解决方案。

这个没啥说的,主要就是登录商户管理平台,查看消息通知,按要求进行整改。

小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。