微信小程序的支付跟H5的支付和APP支付流程不一样,本次只描述下小程序支付流程。
一.账号准备
1.微信小程序账号
文档:小程序申请
小程序支付需要先认证,如果你有已认证的公众号,也可以通过公众号免费注册认证小程序。
一般300元,我是认证的政府的免费。
然后登录小程序,设置APPSecret,记录好自己的AppID和 APPSecret。
2.商户账号申请
申请地址:https://pay.weixin.qq.com/index.php/core/info
准备好要求的各种资质,申请好账号,然后登录,点击账户中心-》API安全
申请证书和AIPv3秘钥,v2现在有淘汰趋势,不需要申请v2,直接v3走起。
ps:我到处网上查询支付文档时候,很多写的是v2的调用方式,还有v3的,还不标明是用哪种方法调用的,导致我蒙蔽了好久。我这次只用v3,不涉及v2
记录好秘钥和下载好证书;
也可以看看文档:小程序支付接入前准备
里面说了有直连模式和服务商模式,并建议多商户的那种应用用服务商模式。但是我不想那么麻烦,虽然我的也是多商户,但是我选择配置多个商户信息,还是直连模式
3.小程序绑定商户号
如果是多商户模式,需要小程序依次绑定多个商户号
二.开发
1.后台调用支付文档:JSAPI下单
文档里写着调用支付需要的一堆参数;把一堆参数拼好请求接口,返回一个prepay_id,这个id是小程序调用支付时要用到的。
2.引入maven依赖
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.8</version>
</dependency>
3.商户配置表
首先因为我是多商户模式的,所以我把商户信息都放在一张表里了,设计表是这样的:
秘钥和证书路径都在表里了,这样做不够安全,严谨的小伙伴可以自行加密
单商户的也可以用这个表,只写一条数据就行了
4.代码
写个工具类,也不算工具类,因为里面写了查询逻辑啥的:
package com.bomc.energy.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bomc.energy.mapper.EnergyMapper;
import com.bomc.energy.service.EnergyService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.wxpay.sdk.WXPayUtil;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;
import static org.springframework.util.FileCopyUtils.BUFFER_SIZE;
/**
* 微信支付工具类
*/
@Component
public class WxPayUtils {
/* @Value("${PrivateKeyPath}")
private String PrivateKeyPath;*/
@Value("${AppID}")
private String appId;
//appid我直接从application.properties中取了,其他证书秘钥啥的都是直接用mapper查库了
/*
@Value("${MerchantId}")
private String mchId;
@Value("${WechatNotifyUrl}")
private String WechatNotifyUrl;
@Value("${serialNo}")
private String serial_no;
@Value("${AppKeyV2}")
private String AppKeyV2;
@Value("${AppKeyV3}")
private String AppKeyV3;*/
@Autowired
private EnergyMapper energyMapper;
/**
* 获取微信预支付订单号
* @return
*/
public Map<String, String> initWechatPay(Map<String, Object> wxOrder) throws IOException, SignatureException {
Map<String, String> param = new HashMap<>();
Map<String, Object> params = new HashMap<>();
JSONObject jsonObject = new JSONObject();
JSONObject amountJsonObject = new JSONObject();
JSONObject payerJsonObject = new JSONObject();
amountJsonObject.put("total",wxOrder.get("total"));
payerJsonObject.put("openid",wxOrder.get("openId"));
try {
//因为是多商户,supplierId 为商户的id
params.put("supplierId",wxOrder.get("supplierId"));
// 应用ID
jsonObject.put("appid",appId);
// 商户号,直接根据商户id去库里查出对应的商户号,下面这种查询的均是这种逻辑
jsonObject.put("mchid",energyMapper.getEnergyMerchantSettingById(params).get("mch_id"));
// 商品描述
jsonObject.put("description","商品购买");
// 商户订单号
jsonObject.put("out_trade_no",wxOrder.get("orderId"));
// 通知地址,支付成功后的回调地址
jsonObject.put("notify_url",energyMapper.getEnergyMerchantSettingById(params).get("notify_url"));
// 订单金额信息
jsonObject.put("amount",amountJsonObject);
// 支付者信息
jsonObject.put("payer",payerJsonObject);
String body = jsonObject.toString();
System.out.println(body);
//微信支付规定访问接口需要设置请求头 Authorization
String authorization ="WECHATPAY2-SHA256-RSA2048 "+
signStr("POST", "/v3/pay/transactions/jsapi", body,params);
String result=HttpUtil.doPostJson("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi",
body,authorization);
JSONObject jsonObject1 = JSON.parseObject(result);
String prepay_id = jsonObject1 == null ? "":(String) jsonObject1.get("prepay_id");
if (!prepay_id.equals("")){
//返回结果格式参照https://uniapp.dcloud.io/api/plugins/payment?id=requestpayment
//随机字符串
//时间戳,但是单位为s,不是毫秒
String timeStamp=String.valueOf(System.currentTimeMillis() / 1000);
String nonceStr=WXPayUtil.generateNonceStr();
param.put("appId", appId);
param.put("timeStamp", timeStamp);
param.put("nonceStr", nonceStr);
param.put("package", "prepay_id=" + prepay_id);
//param.put("signType", "RSA");
//给前端拼接paySign
String message = appId + "\n"
+ timeStamp + "\n"
+ nonceStr + "\n"
+ "prepay_id=" + prepay_id + "\n";
String signature = sign(message.getBytes("utf-8"),params);
param.put("paySign", signature);
System.out.println(param);
return param;
}
} catch (Exception e) {
e.printStackTrace();
}
return param;
}
/*public String signStr2(String method, String url){
String nonceStr = UUID.randomUUID().toString();
long timestamp = System.currentTimeMillis() / 1000;
String message = buildMessage2(method, url, timestamp, nonceStr);
String signature = null;
try {
signature = sign(message.getBytes("utf-8"));
} catch (SignatureException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("签名后:[" + signature + "]");
return "mchid=\"" + mchId + "\","
+ "serial_no=\"" + serial_no + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "signature=\"" + signature + "\"";
}*/
public String buildMessage2(String method, String url, long timestamp, String nonceStr) {
String str = method + "\n"
+ url + "\n"
+ timestamp + "\n"
+ nonceStr + "\n\n";
System.out.println("签名数据[" + str + "]");
return str;
}
public String signStr(String method, String url, String body,Map<String,Object> params){
String nonceStr = UUID.randomUUID().toString();
long timestamp = System.currentTimeMillis() / 1000;
String message = buildMessage(method, url, timestamp, nonceStr, body);
System.out.println("message:[" + message + "]");
String signature = null;
try {
signature = sign(message.getBytes("utf-8"),params);
} catch (SignatureException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("signature=[" + signature + "]");
return "mchid=\"" + energyMapper.getEnergyMerchantSettingById(params).get("mch_id") + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "serial_no=\"" + energyMapper.getEnergyMerchantSettingById(params).get("serial_no") + "\","
+ "signature=\"" + signature + "\"";
}
public String buildMessage(String method, String url, long timestamp, String nonceStr, String body) {
String str = method + "\n"
+ url + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ body + "\n";
return str;
}
public String sign(byte[] message,Map<String,Object> params) throws SignatureException {
Signature sign = null;
try {
sign = Signature.getInstance("SHA256withRSA");
//证书方式获取
PrivateKey privateKey = getPrivateKey(params);
sign.initSign(privateKey);
sign.update(message);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 获取私钥。
*/
/*public static PrivateKey getPrivateKey() throws IOException {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
new ByteArrayInputStream(privateKey.getBytes("utf-8")));
return merchantPrivateKey;
}*/
/**
* 获取私钥。
*
* ///@param filename 私钥文件路径 (required)
* @return 私钥对象
* <p>
* 完全不需要修改,注意此方法也是去掉了头部和尾部,注意文件路径名
*/
public PrivateKey getPrivateKey(Map<String,Object> params) throws IOException {
try {
String PrivateKeyPath=energyMapper.getEnergyMerchantSettingById(params).get("private_key_path").toString();
String content = new String(Files.readAllBytes(Paths.get(PrivateKeyPath)), "utf-8");
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("无效的密钥格式");
}
}
// xml解析
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if(null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if(children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
/**
* 获取子结点的xml
*
* @param children
* @return String
*/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if(!children.isEmpty()) {
Iterator it = children.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if(!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
/*支付通知和退款通知给服务器的回调 请求头验签*/
public boolean signVerify(String serialNumber, String message, String signature ,String supplierId) {
Map<String, Object> params = new HashMap<>();
try {
params.put("supplierId",supplierId);
String mchId=energyMapper.getEnergyMerchantSettingById(params).get("mch_id").toString();
String AppKeyV3=energyMapper.getEnergyMerchantSettingById(params).get("app_key_v3").toString();
String PrivateKeyPath=energyMapper.getEnergyMerchantSettingById(params).get("private_key_path").toString();
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(PrivateKeyPath));
// 获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(mchId,
new WechatPay2Credentials(mchId,
new PrivateKeySigner(energyMapper.getEnergyMerchantSettingById(params).get("serial_no").toString(), merchantPrivateKey)),
AppKeyV3.getBytes(StandardCharsets.UTF_8));
// 从证书管理器中获取verifier
Verifier verifier = certificatesManager.getVerifier(mchId);
return verifier.verify(serialNumber, message.getBytes(StandardCharsets.UTF_8), signature);
} catch (HttpCodeException | NotFoundException | IOException | GeneralSecurityException e) {
e.printStackTrace();
}
return false;
}
/*支付通知和退款通知给服务器的回调 解密报文*/
public String decryptOrder(String body,String supplierId) {
try {
Map<String, Object> params = new HashMap<>();
params.put("supplierId",supplierId);
String AppKeyV3=energyMapper.getEnergyMerchantSettingById(params).get("app_key_v3").toString();
AesUtil util = new AesUtil(AppKeyV3.getBytes(StandardCharsets.UTF_8));
ObjectMapper objectMapper = new ObjectMapper();
JsonNode node = objectMapper.readTree(body);
JsonNode resource = node.get("resource");
String ciphertext = resource.get("ciphertext").textValue();
String associatedData = resource.get("associated_data").textValue();
String nonce = resource.get("nonce").textValue();
return util.decryptToString(associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext);
} catch (JsonProcessingException | UnsupportedEncodingException | GeneralSecurityException e) {
e.printStackTrace();
}
return null;
}
public static String read(Reader reader) throws IOException
{
StringWriter writer = new StringWriter();
try
{
write(reader, writer);
return writer.getBuffer().toString();
}
finally{ writer.close(); }
}
/**
* write.
*
* @param reader Reader.
* @param writer Writer.
* @return count.
* @throws IOException
*/
public static long write(Reader reader, Writer writer) throws IOException
{
return write(reader, writer, BUFFER_SIZE);
}
/**
* write.
*
* @param reader Reader.
* @param writer Writer.
* @param bufferSize buffer size.
* @return count.
* @throws IOException
*/
public static long write(Reader reader, Writer writer, int bufferSize) throws IOException
{
int read;
long total = 0;
char[] buf = new char[BUFFER_SIZE];
while( ( read = reader.read(buf) ) != -1 )
{
writer.write(buf, 0, read);
total += read;
}
return total;
}
}
http请求工具类:
package com.bomc.energy.util;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HttpUtil {
public static String doGet(String url) { // 无参数get请求
return doGet(url, null);
}
public static String doGet(String url, Map<String, String> param) { // 带参数get请求
CloseableHttpClient httpClient = HttpClients.createDefault(); // 创建一个默认可关闭的Httpclient 对象
String resultMsg = ""; // 设置返回值
CloseableHttpResponse response = null; // 定义HttpResponse 对象
try {
URIBuilder builder = new URIBuilder(url); // 创建URI,可以设置host,设置参数等
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
HttpGet httpGet = new HttpGet(uri); // 创建http GET请求
// httpGet.setHeader(key,value); //设置请求的请求头
response = httpClient.execute(httpGet); // 执行请求
if (response.getStatusLine().getStatusCode() == 200) { // 判断返回状态为200则给返回值赋值
resultMsg = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally { // 不要忘记关闭
try {
if (response != null) {
response.close();
}
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultMsg;
}
public static String doGet(String url, Map<String, String> param ,String authorization) { // 带参数get请求
CloseableHttpClient httpClient = HttpClients.createDefault(); // 创建一个默认可关闭的Httpclient 对象
String resultMsg = ""; // 设置返回值
CloseableHttpResponse response = null; // 定义HttpResponse 对象
try {
URIBuilder builder = new URIBuilder(url); // 创建URI,可以设置host,设置参数等
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
HttpGet httpGet = new HttpGet(uri); // 创建http GET请求
//设置请求的请求头
httpGet.setHeader("Content-type", "application/json");
httpGet.setHeader("Accept", "application/json");
httpGet.addHeader("Authorization",authorization);
response = httpClient.execute(httpGet); // 执行请求
if (response.getStatusLine().getStatusCode() == 200) { // 判断返回状态为200则给返回值赋值
resultMsg = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally { // 不要忘记关闭
try {
if (response != null) {
response.close();
}
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultMsg;
}
public static String doPost(String url) { // 无参数post请求
return doPost(url, null);
}
public static String doPost(String url, Map<String, String> param) {// 带参数post请求
CloseableHttpClient httpClient = HttpClients.createDefault(); // 创建一个默认可关闭的Httpclient 对象
CloseableHttpResponse response = null;
String resultMsg = "";
try {
HttpPost httpPost = new HttpPost(url); // 创建Http Post请求
// httpPost.setHeader(key,value); //设置post请求的请求头
if (param != null) { // 创建参数列表
List<NameValuePair> paramList = new ArrayList<NameValuePair>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);// 模拟表单
httpPost.setEntity(entity);
}
response = httpClient.execute(httpPost); // 执行http请求
if (response.getStatusLine().getStatusCode() == 200) {
resultMsg = EntityUtils.toString(response.getEntity(), "utf-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultMsg;
}
public static String doPostJson(String url, String json) {
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
HttpPost httpPost = new HttpPost(url);
// httpPost.setHeader(key,value); //设置post请求的请求头
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); //指定传输参数为json
httpPost.setEntity(entity);
response = httpClient.execute(httpPost);
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doPostJson(String url, String json ,String authorization) {
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Content-type", "application/json");
httpPost.setHeader("Accept", "application/json");
httpPost.addHeader("Authorization",authorization);
// httpPost.setHeader(key,value); //设置post请求的请求头
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); //指定传输参数为json
httpPost.setEntity(entity);
response = httpClient.execute(httpPost);
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
System.out.println("返回结果:"+resultString);
}else{
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
System.out.println("返回结果:"+resultString);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String postXmlRequest(String url, String xml) throws Exception {
HttpPost post = new HttpPost(url);
post.setHeader("Content-type", "text/xml");
post.setEntity(new StringEntity(xml));
post.setEntity(new StringEntity(xml, "UTF-8"));
CloseableHttpClient client = HttpClients.createDefault();
CloseableHttpResponse response = client.execute(post);
return response.getStatusLine().getStatusCode() == 200 ? EntityUtils.toString(response.getEntity(), "UTF-8") : null;
}
}
触发付款controller:获取订单号 openid啥的自行发挥,看一下怎么调wxPayUtils.initWechatPay 方法就行了。
/**微信公众号支付统一下单接口,订单已完成,用户点击付款的时候触发这个
*/
@RequestMapping(value = "initWechatPay", method = RequestMethod.POST)
@ResponseBody
public Object initWechatPay(@RequestParam Map<String,Object> params, HttpServletRequest req) {
RetBase ret = new RetBase();
String orderId=params.get("orderId").toString();
String openId="";
String token = req.getHeader("token");
String supplierId="";
try {
if(isNotNull(token)){
String userId = JWT.decode(token).getAudience().get(0);
if(isNotNull(userId)){
params.put("userId",userId);
EnergyUser user=loginService.getMiniUserById(params);
openId=user.getOpenId();
}
}else{
ret.setCode("-1");
ret.setMsg("用户未登录");
ret.setSuccess(false);
return ret;
}
//获取订单详情
BigDecimal actualAmount = null;
String out_trade_no = null;
if(!StringUtils.isEmpty(orderId)) {
List<Map<String,Object>> orderList=energyService.getUserOrderList(params);
if(isNotNull(orderList)){
Map<String,Object> order=orderList.get(0);
//订单金额
actualAmount=new BigDecimal(order.get("money").toString()).setScale(2, BigDecimal.ROUND_HALF_UP);
//商户id
supplierId=order.get("supplier_id").toString();
}
out_trade_no = orderId;
}else{
ret.setCode("-1");
ret.setMsg("未获取订单信息");
ret.setSuccess(false);
return ret;
}
//初始化微信统一下单接口
Map<String, Object> wxParam=new HashMap<>();
wxParam.put("total",actualAmount.multiply(new BigDecimal(100))
.intValue());
wxParam.put("openId",openId);
wxParam.put("orderId",orderId);
wxParam.put("supplierId",supplierId);
Map<String,String> result=wxPayUtils.initWechatPay(wxParam);
if(isNotNull(result)){
ret.setData(result);
ret.setCode("0");
ret.setMsg("下单成功");
ret.setSuccess(true);
}else{
ret.setCode("-1");
ret.setMsg("下单失败");
ret.setSuccess(false);
}
} catch (Exception e) {
e.printStackTrace();
ret.setCode("-10");
ret.setMsg("程序错误");
ret.setSuccess(false);
}
return ret;
}
小程序调用起支付文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml
而uniapp中 uni.requestPayment方法集成了小程序调用支付,
uniapp 前端调用支付:
//确认支付
confirm: async function() {
let self=this;
//alert(retUrl)
var data= {
orderId:this.orderId,
}
self.$request.TokenRequest({url:"energy/initWechatPay"}, data).then(res => {
if (res.success) {
var data = res.data;
console.log(res.data);
uni.requestPayment({
provider: 'wxpay',
timeStamp: data.timeStamp,
nonceStr: data.nonceStr,
package: data.package,
signType: 'RSA',
paySign: data.paySign,
success: function(res) {
console.log(res)
uni.showToast({
title: '支付成功',
duration: 1000
});
uni.redirectTo({
url: '/pages/money/paySuccess'
})
},
fail: function(err) {
console.log(err)
uni.showToast({
title: '支付失败'+err.errMsg,
icon:'none',
duration: 2500
});
console.log('fail:' + JSON.stringify(err));
}
});
} else {
console.log(res.body.status.errorDesc);
}
},error => {uni.showToast({
icon:'none',
title: error,
duration: 2000
}); }).catch(err=>{uni.showToast({
icon:'none',
title: err,
duration: 2000
}); })
/* uni.redirectTo({
url: '/pages/money/paySuccess'
}) */
},
这样调用支付就完成了;
支付完程序需要监听回调方法,从而改订单状态是否已付款;
controller方法:
/**
* 支付通知(回调)
* @param request
* @param response
* @return
* @throws Exception
*/
@RequestMapping(value = "payNotifyUrl/{supplierId}", method = RequestMethod.POST)
@ResponseBody
public JSONObject payNotifyUrl(@PathVariable String supplierId, HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
JSONObject jsonObject = new JSONObject();
try {
System.out.println("Wechatpay-Timestamp:" + request.getHeader("Wechatpay-Timestamp"));
System.out.println("Wechatpay-Nonce:" + request.getHeader("Wechatpay-Nonce"));
System.out.println("Wechatpay-Signature:" + request.getHeader("Wechatpay-Signature"));
System.out.println("Wechatpay-Serial:" + request.getHeader("Wechatpay-Serial"));
Map<String, String> result = new HashMap<String, String>();
result.put("code", "FAIL");
StringBuilder signStr = new StringBuilder();
signStr.append(request.getHeader("Wechatpay-Timestamp")).append("\n");
signStr.append(request.getHeader("Wechatpay-Nonce")).append("\n");
BufferedReader br = request.getReader();
String str = null;
StringBuilder builder = new StringBuilder();
while ((str = br.readLine()) != null) {
builder.append(str);
}
System.out.println(builder.toString());
signStr.append(builder.toString()).append("\n");
//进行验签,确保请求来自微信
if (!wxPayUtils.signVerify(request.getHeader("Wechatpay-Serial"),
signStr.toString(), request.getHeader("Wechatpay-Signature"),supplierId)) {
System.out.println("PayCallback==>>sign error");
result.put("message", "sign error");
jsonObject.put("code","FAIL");
jsonObject.put("message","sign error");
return jsonObject;
}
System.out.println("PayCallback==>>sign success");
//解密报文
String info = wxPayUtils.decryptOrder(builder.toString(),supplierId);
System.out.println(info);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode node = objectMapper.readTree(info);
//可以解密出很多参数,具体见[官方文档](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_5.shtml)
String orderId = node.get("out_trade_no").toString().substring(1, node.get("out_trade_no").toString().length() - 1);
String bankType = node.get("bank_type").toString().substring(1, node.get("bank_type").toString().length() - 1);
Map<String,Object> params=new HashMap<>();
params.put("id",orderId);
if(!StringUtils.isEmpty(orderId)) {
List<Map<String,Object>> orderList=energyService.getUserOrderList(params);
if(isNotNull(orderList)){
Map<String,Object> order=orderList.get(0);
if(order.get("status").equals("1")){
params.put("status","2");
energyService.updateUserOrder(params);
}
}
}
result.put("code", "SUCCESS");
jsonObject.put("code","SUCCESS");
jsonObject.put("message","SUCCESS");
} catch (IOException e) {
e.printStackTrace();
jsonObject.put("code","FAIL");
jsonObject.put("message","支付失败");
return jsonObject;
} finally {
}
return jsonObject;
}
因为是多商户, 且回调url中不能有参数,所以只能把商户id写到url中,因为回调返回的信息需要用到该商户的v3秘钥解密才行,所以必须知道商户id;
在库里是这么配置的:
这样完整的支付流程就完成了;
因为距离开发这玩意儿挺久了,有一些细节可能没想起来,有问题及时留言联系