前期
请参考上一篇 关于使用微信公众号后端springboot项目对接微信支付v3 jsapi [配置参数篇]
环境
最近在学习微信支付这方面的知识,经过重重试错,终于成功,现在分享出来让大家借鉴
ps:我开发的是微信公众号调用jsapi微信支付v3的业务逻辑
JSAPI支付产品介绍
开发环境
- jdk8
- springboot2
- 若依
- 微信支付版本
<!-- 微信支付的sdk-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.8</version>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.9</version>
</dependency>
DTO 实体
这个是自己微信的openid ,一定要和调用者的微信id相同,不能会调支付失败
@Data
public class PayDTO {
@NotBlank(message = "微信code不能为空")
private String openId;
}
Service 层
这三个注入的类已经在上一篇配置好了
@Service
@Slf4j
public class WxPayService{
@Autowired
private WxPayConfig wxPayConfig;
@Autowired
private CloseableHttpClient httpClient;
@Autowired
private PrivateKey privateKey;
private PrivateKey getPrivateKey(){
try {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
new FileInputStream(wxPayConfig.getPrivateKeyPath()));
return merchantPrivateKey;
} catch (FileNotFoundException e) {
throw new RuntimeException("私钥文件不存在",e);
}
}
@Override
public Map<String, Objects> nativePay(String openId) throws IOException, GeneralSecurityException, HttpCodeException, NotFoundException {
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
// jsapi 的接口
// String reqdata = "{"
// + "\"amount\": {"
// + "\"total\": 1,"
// + "\"currency\": \"CNY\""
// + "},"
// + "\"mchid\": \"1900006891\","
// + "\"description\": \"Image形象店-深圳腾大-QQ公仔\","
// + "\"notify_url\": \"https://www.weixin.qq.com/wxpay/pay.php\","
// + "\"payer\": {"
// + "\"openid\": \"o4GgauE1lgaPsLabrYvqhVg7O8yA\"" + "},"
// + "\"out_trade_no\": \"1217752501201407033233388882\","
// + "\"goods_tag\": \"WXG\","
// + "\"appid\": \"wxdace645e0bc2c424\"" + "}";
Map map = new HashMap();
Map amountMap = new HashMap();
amountMap.put("total",1); //支付金额
amountMap.put("currency","CNY"); //支付币种 默认人民币
map.put("amount",amountMap);
map.put("mchid",wxPayConfig.getMchId());
map.put("description","测试1"); //支付描述
map.put("notify_url",wxPayConfig.getNotifyDomain()); //支付成功回调api
Map payerMap = new HashMap();
payerMap.put("openid",openId); // 支付者的openid
map.put("payer",payerMap);
map.put("out_trade_no", RandomUtil.randomNumbers(28)); //交易号 每个商户号不能有重复的交易号
map.put("goods_tag","WXG");
map.put("appid",wxPayConfig.getAppid()); //微信公众号 appid
Gson gson = new Gson();
String jsonStr = gson.toJson(map);
StringEntity entity = new StringEntity(jsonStr,"utf-8"); //转化为requestbody
entity.setContentType("application/json"); //使用json
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json"); //协议切换
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost); //执行发送
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
//这一部分 我建议是后台完成 ,因为根据文档,前端支付的时候需要
// 公众号appid ,时间戳,随机字符串和 后端发送请求微信返回的prepay_id和签名
// 所以可以在这次调用时候,使用map 一起返回前端 ,让前端自己组装
String responseStr = EntityUtils.toString(response.getEntity());
System.out.println("success,return body = " + responseStr);
JSONObject jsonObject = JSONUtil.parseObj(responseStr);
String prepay_id = jsonObject.getStr("prepay_id");
// 获取时间戳
long timeL = System.currentTimeMillis() / 1000;
// 获取随机字符串
String nonceStr = TokenTool.getToken();
// 新建一个支付参数map
Map req = new HashMap();
req.put("appId", wxPayConfig.getAppid());
req.put("timeStamp", timeL);
req.put("nonceStr", nonceStr);
req.put("package", "prepay_id=" + prepay_id);
// 设置加密方式
req.put("signType", "RSA");
// 获取加密后的密文
String paySign = RSAUtil.getToken(wxPayConfig.getAppid(), timeL, nonceStr,"prepay_id=" + prepay_id, privateKey);
req.put("paySign", paySign);
return req;
} else if (statusCode == 204) {
System.out.println("success");
return null;
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
response.close();
}
}
}
Controller层 编写
可以先做一个demo微信支付Controller,测试好再进行具体逻辑修改
ps :该支付接口一定不要有任何认证和拦截 ,不能微信服务器调用不起来
@R
estController
@RequestMapping("/test/wxpay")
@Api(tags = "[测试]微信支付功能相关接口")
@Slf4j
@CrossOrigin
public class TestControllerByWxPay {
@Autowired
private WxPayConfig wxPayConfig;
@Autowired
private WxPayService wxPayService;
@GetMapping("/d1")
@Anonymous
@ApiOperation(value = "第一个测试[匿名]")
public AjaxResult d1(){
return AjaxResult.success(wxPayConfig.getAppid());
}
@PostMapping("/navtivepay")
@ApiOperation(value = "微信jsapi支付接口[匿名]")
@Anonymous
public AjaxResult nativePay(@Validated @RequestBody PayDTO payDTO){
Map<String, Objects> map = null;
log.info(payDTO.getOpenId());
try {
map = wxPayService.nativePay(payDTO.getOpenId());
} catch (IOException e) {
throw new RuntimeException("还是报错了",e);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} catch (NotFoundException e) {
throw new RuntimeException(e);
} catch (HttpCodeException e) {
throw new RuntimeException(e);
}
return AjaxResult.success(map);
}
}
结语
在下一篇 我会对前端demo代码测试微信支付,进行讲解