先上个微信小程序支付官方文档地址:
https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_7&index=8
重点看以下几点:
1)支付流程及业务流程;
2)支付接口
3)统一下单接口
4)回调接口
5)交易查询接口
2、现在开始上demo
1)首先引入微信官网开发包java版
Maven引入:
<dependency>
<groupId>com.weixin</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
2)微信参数配置
WxpayConfig.java
/**
* 微信开发平台应用ID
*/
public static final String APPID = "微信公众号小程序id";
/**
* 微信支付商户号
*/
public static final String MCH_ID = "商户支付平台获取";
/**
* 应用对应的凭证
*/
public static final String APP_SECRET = "登录微信公众平台获取";
/**
* 应用对应的密钥
*/
public static final String APP_KEY = "32位支付密匙,在商户支付平台产品安全--应用密匙里设置";
/**
* 微信服务器回调通知URL
*/
public static final String NOTIFY_URL = "https://.../notifyUrl.action";
3)微信帮助类
加签、验签及支付请求等方法
PayCommonUtil.java
public class PayCommonUtil {
/**
* post请求并得到返回结果
* @param requestUrl
* @param requestMethod
* @param output
* @return
*/
public static String httpsRequest21(String requestUrl, String requestMethod, String output) {
try{
URL url = new URL(requestUrl);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setRequestMethod(requestMethod);
if (null != output) {
OutputStream outputStream = connection.getOutputStream();
outputStream.write(output.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = connection.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
connection.disconnect();
return buffer.toString();
}catch(Exception ex){
ex.printStackTrace();
}
return "";
}
// 随机字符串生成
public static String getRandomString(int length) { // length表示生成字符串的长度
String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
// 请求xml组装
public static String getRequestXml(SortedMap<String, Object> parameters) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String key = (String) entry.getKey();
// String value = (String) entry.getValue();
Object value = entry.getValue();
// || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)
if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) {
sb.append("<" + key + ">" + "<![CDATA[" + value + "]]></" + key + ">");
} else {
sb.append("<" + key + ">" + value + "</" + key + ">");
}
}
sb.append("</xml>");
return sb.toString();
}
// 生成签名
public static String createSign(String characterEncoding, SortedMap<String, Object> parameterMap) {
if (parameterMap == null) {
return null;
}
StringBuffer sb = new StringBuffer();
List<String> keys = new ArrayList<>(parameterMap.keySet());
Collections.sort(keys);
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
// String value = (String) parameters.get(key);
Object value = parameterMap.get(key);
// if (Tools.notEmpty(value)) {
sb.append((i == 0 ? "" : "&") + key + "=" + value);
// }
}
sb.append("&key=" + WxpayConfig.APP_KEY);
System.out.println("【生成签名 】" + sb.toString());
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
// 微信支付请求
public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
try {
URL url = new URL(requestUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
// 当outputStr不为null时向输出流写数据
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
conn.disconnect();
return buffer.toString();
} catch (ConnectException ce) {
System.out.println("连接超时:" + ce);
} catch (Exception e) {
System.out.println("https请求异常" + e);
}
return null;
}
// 退款的请求方法
public static String httpsRequest2(String requestUrl, String requestMethod, String outputStr) throws Exception {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
StringBuilder res = new StringBuilder("");
FileInputStream instream = new FileInputStream(new File("/home/apiclient_cert.p12"));
try {
keyStore.load(instream, "".toCharArray());
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, "1313329201".toCharArray()).build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
try {
HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
httpost.addHeader("Connection", "keep-alive");
httpost.addHeader("Accept", "*/*");
httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httpost.addHeader("Host", "api.mch.weixin.qq.com");
httpost.addHeader("X-Requested-With", "XMLHttpRequest");
httpost.addHeader("Cache-Control", "max-age=0");
httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
StringEntity entity2 = new StringEntity(outputStr, Consts.UTF_8);
httpost.setEntity(entity2);
System.out.println("executing request" + httpost.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httpost);
try {
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: " + entity.getContentLength());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
String text = "";
res.append(text);
while ((text = bufferedReader.readLine()) != null) {
res.append(text);
System.out.println(text);
}
}
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
return res.toString();
}
// xml解析
public static SortedMap<String, Object> doXMLParse(String strxml){
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if (null == strxml || "".equals(strxml)) {
return null;
}
SortedMap<String, Object> m = new TreeMap<>();
InputStream in;
try {
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();
} catch (Exception e1) {
e1.printStackTrace();
}
return m;
}
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();
}
/**
* 验证微信回调签名
* @Description
* @Author zhaozhenhua
* @date 2018年5月10日
* @param pd
* @return
* @throws Exception
*/
public static boolean checkWXSign(SortedMap<String, Object> receiveMap) {
String signFromAPIResponse = (String) receiveMap.get("sign");
if (StringUtils.isEmpty(signFromAPIResponse)) {
System.out.println("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
return false;
}
//清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
receiveMap.remove("sign");
String responseSign = createSign("utf-8", receiveMap);
if (!responseSign.equals(signFromAPIResponse)) {
//签名验不过,表示这个API返回的数据有可能已经被篡改了
System.out.println("API返回的数据签名验证不通过,有可能被第三方篡改!!! responseSign生成的签名为" + responseSign);
return false;
}
System.out.println("服务器回包里面的签名是:" + signFromAPIResponse);
return true;
}
4)工具类
MD5Util.java
MD5Util.java
package com.axyd.utils;
import java.security.MessageDigest;
public class MD5Util {
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString;
}
private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}
3、前端调用
Controller:
WxPayController.java
package com.axyd.controller;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.axyd.pojo.Activity;
import com.axyd.pojo.Orders;
import com.axyd.pojo.User;
import com.axyd.pojo.dto.ActivityDto;
import com.axyd.service.ActivityService;
import com.axyd.service.AuthTokenService;
import com.axyd.service.OrdersService;
import com.axyd.service.UserService;
import com.axyd.utils.DateUtils;
import com.axyd.utils.NumberUtils;
import com.axyd.utils.PayCommonUtil;
import com.axyd.utils.StringUtils;
import com.axyd.utils.WxpayConfig;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
/**
*
* <p>Title: 微信pay</p>
* <p>Description: </p>
* @author zengyuan
* @version 1.0
* @date 2019年4月27日 上午11:24:59
*/
@Controller
@RequestMapping("/wx")
public class WxPayController {
protected static Logger log = Logger.getLogger(WxPayController.class);
@Autowired
private OrdersService ordersService;
@Autowired
private UserService userService;
@Autowired
private ActivityService activityService;
@Autowired
private AuthTokenService authTokenService;
/**
* 微信统一下单接口
* @param request
* @param response
* @return
*/
@RequestMapping("/pay")
@ResponseBody
public Map<String,Object> details(HttpServletRequest request,HttpServletResponse response){
String resultCode1 = "1";// 返回结果 1成功,0失败
String errorMessage = "";//返回错误信息
Map<String, Object> json=new HashMap<String, Object>();
String id=request.getParameter("id");//订单id
try{
String[] param = {id};
String[] pname = {"id"};
if(!"".equals(StringUtils.isEmpty(param, pname))){
errorMessage += StringUtils.isEmpty(param, pname);
resultCode1 = "0";
}
if("" == errorMessage){
//查询订单
Orders orders=new Orders();
orders.setId(Integer.parseInt(id));
orders = ordersService.list(orders).get(0);
User user=new User();
user.setId(orders.getUserid());
user=userService.list(user).get(0);
//查询活动ID
ActivityDto activity=new ActivityDto();
activity.setId(orders.getActivityid());
Activity activity1=(Activity) activityService.list(activity).get(0);
// 预支付
InetAddress ia = InetAddress.getLocalHost();
String localip=ia.getHostAddress();
String trade_no=orders.getCode();
String openid=user.getOpenid();
SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
parameterMap.put("appid", WxpayConfig.APPID);
parameterMap.put("mch_id", WxpayConfig.MCH_ID);
parameterMap.put("nonce_str", PayCommonUtil.getRandomString(32));
parameterMap.put("body", activity1.getName());
parameterMap.put("out_trade_no", trade_no);
parameterMap.put("fee_type", "CNY");
// BigDecimal total = totalAmount.multiply(new BigDecimal(100));
// parameterMap.put("total_fee", total.intValue()+"");
parameterMap.put("total_fee", 1+"");
parameterMap.put("spbill_create_ip", localip);
parameterMap.put("notify_url", WxpayConfig.NOTIFY_URL);
parameterMap.put("trade_type", "JSAPI");
parameterMap.put("openid", openid);
parameterMap.put("sign_type", "MD5");
// parameterMap.put("attach", pd.get("ORDERTYPE"));//附加数据
String sign = PayCommonUtil.createSign("UTF-8", parameterMap);
parameterMap.put("sign", sign);
String requestXML = PayCommonUtil.getRequestXml(parameterMap);
System.out.println("【转换为xml格式的参数】 " + requestXML);
String resultXML = PayCommonUtil.httpsRequest(
"https://api.mch.weixin.qq.com/pay/unifiedorder",
"POST", requestXML);
System.out.println("【返回的XML】 " + resultXML);
Map mapResult = PayCommonUtil.doXMLParse(resultXML);
String returnCode = (String) mapResult.get("return_code");
System.out.println("【返回内容 】 " + returnCode);
if ("SUCCESS".equals(returnCode)) {
String resultCode = (String) mapResult.get("result_code");
if ("SUCCESS".equals(resultCode)) {
String prepay_id = (String) mapResult.get("prepay_id");
//二次加签
SortedMap<String, Object> finalpackage = new TreeMap<String, Object>();
finalpackage.put("appId", WxpayConfig.APPID);
String noncestr = PayCommonUtil.getRandomString(32);
finalpackage.put("nonceStr", noncestr);
String timeStamp = String.valueOf(System
.currentTimeMillis() / 1000);
finalpackage.put("package", "prepay_id=" + prepay_id);
finalpackage.put("signType", "MD5");
finalpackage.put("timeStamp", timeStamp);
sign = PayCommonUtil.createSign("UTF-8", finalpackage);
SortedMap<String, Object> returnmap = new TreeMap<String, Object>();
returnmap.put("appId", WxpayConfig.APPID);
returnmap.put("partnerId", WxpayConfig.MCH_ID);
returnmap.put("prepayId", prepay_id);
returnmap.put("nonceStr", noncestr);
returnmap.put("timeStamp", timeStamp);
returnmap.put("package", "Sign=WXPay");
returnmap.put("sign", sign);
returnmap.put("out_trade_no", trade_no);
json.put("result", returnmap);
} else {
String errCodeDes = (String) mapResult
.get("err_code_des");
json.put("errCodeDes", errCodeDes);
}
} else {
String returnMsg = (String) mapResult.get("return_msg");
json.put("returnMsg", returnMsg);
}
}
} catch (Exception e) {
log.error("微信统一支付接口异常",e);
errorMessage += "接口调用异常";
resultCode1 = "0";
}
json.put("errorMessage", errorMessage);
json.put("resultCode", resultCode1);
return json;
}
/**
* 回调函数
* @param request
* @param response
* @return
* @throws Exception
*/
@RequestMapping("/notifyUrl")
@ResponseBody
public String notifyUrl(HttpServletRequest request, HttpServletResponse response) throws Exception{
System.out.println("jinruhuitiaodizhile");
//商户处理后同步返回给微信参数:
String xmlResultSuccess = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
String xmlResultFailure = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
SortedMap<String, Object> params = null;
try {
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
String resultxml = new String(outSteam.toByteArray(), "utf-8");
params = PayCommonUtil.doXMLParse(resultxml);
System.out.println("微信回调的信息:解析XML得到key:value");
for (Object b : params.keySet()) {
System.out.println("key="+b+":"+"value="+params.get(b));
}
outSteam.close();
inStream.close();
} catch (Exception e) {
e.printStackTrace();
System.out.println("回调xml解析失败");
}
//验证签名
boolean signVerified = PayCommonUtil.checkWXSign(params);
System.out.println("验证签名:"+signVerified);
if (!signVerified) {
// 支付失败,失败业务逻辑处理
//将订单状态设置为待支付
return xmlResultFailure;
} else {
String return_code = (String) params.get("return_code");//状态
String out_trade_no = (String) params.get("out_trade_no");//订单号
String ORDERTYPE = (String) params.get("attach");//商家数据包
System.out.println("订单号out_trade_no:"+out_trade_no);
if (return_code.equals("SUCCESS")) {
if (out_trade_no != null) {
//查询订单
Orders orders1=new Orders();
orders1.setCode(out_trade_no);
orders1=ordersService.list(orders1).get(0);
//付款成功业务逻辑处理
Orders orders=new Orders();
orders.setId(orders1.getId());;
orders.setParticipationcode(NumberUtils.buildRandom(6)+"");
if(orders1.getIssingle()==1){
orders.setStatus(3);//待分享
}else{
orders.setStatus(1);//待参加
}
orders.setPaytime(DateUtils.getTime()); //更新支付时间
this.ordersService.update(orders); //更新交易表中状态
return xmlResultSuccess;
}
}
}
return null;
}
/**
* 订单查询
* @param request
* @param response
* @return
* @throws Exception
*/
@RequestMapping("/query")
@ResponseBody
public Map<String, Object> query(HttpServletRequest request, HttpServletResponse response) throws Exception{
String resultCode = "1";// 返回结果 1成功,0失败
String errorMessage = "";//返回错误信息
Map<String, Object> json=new HashMap<String, Object>();
String code=request.getParameter("code");//订单号
try{
SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
parameterMap.put("appid", WxpayConfig.APPID);
parameterMap.put("mch_id", WxpayConfig.MCH_ID);
parameterMap.put("nonce_str", PayCommonUtil.getRandomString(32));
parameterMap.put("out_trade_no", code);
String sign = PayCommonUtil.createSign("UTF-8", parameterMap);
parameterMap.put("sign", sign);
String requestXML = PayCommonUtil.getRequestXml(parameterMap);
System.out.println("【转换为xml格式的参数】 "+requestXML);
String resultXML = PayCommonUtil.httpsRequest(
WxpayConfig.ORDER_QUERY_URL,"POST", requestXML);
System.out.println("【返回的XML】 " + resultXML);
Map<String, String> returnMap = WXPayUtil.xmlToMap(resultXML);
String return_code = returnMap.get("return_code");
if (WXPayConstants.SUCCESS.equals(return_code)) {
String appid = returnMap.get("appid");
String mch_id = returnMap.get("mch_id");
String result_code = returnMap.get("result_code");
/* 验签 */
if (WXPayUtil.isSignatureValid(returnMap, WxpayConfig.APP_KEY)) {
if (WxpayConfig.APPID.equals(appid) && WxpayConfig.MCH_ID.equals(mch_id)
&& "SUCCESS".equals(result_code)) {
String trade_state = returnMap.get("trade_state");// 交易状态
String total_fee = returnMap.get("total_fee");// 总金额
String out_trade_no = returnMap.get("out_trade_no");// 商户订单号
String trade_state_desc = returnMap.get("trade_state_desc");// 交易状态描述
/* 状态成功 金额正确 */
if ("SUCCESS".equals(trade_state) && total_fee.equals("1")) {
/* 订单号相同 */
if (out_trade_no.equals(code)) {
//查询订单
Orders orders1=new Orders();
orders1.setCode(out_trade_no);
orders1=ordersService.list(orders1).get(0);
//付款成功业务逻辑处理
Orders orders=new Orders();
orders.setId(orders1.getId());;
orders.setParticipationcode(NumberUtils.buildRandom(6)+"");
if(orders1.getIssingle()==1){
orders.setStatus(3);//待分享
}else{
orders.setStatus(1);//待参加
}
orders.setPaytime(DateUtils.getTime()); //更新支付时间
this.ordersService.update(orders); //更新交易表中状态
}
}
}
}else{
System.out.println("验签未通过");
}
json.put("result", returnMap);
}
} catch (Exception e) {
e.printStackTrace();
errorMessage += "程序崩溃啦";
resultCode = "0";
}
json.put("errorMessage", errorMessage);
json.put("resultCode",resultCode);
return json;
}
}
4、总结
吐槽下
微信相比支付宝而言确实麻烦接入些,文档也写的差强人意,可能毕竟支付宝是专业做支付的吧!毕竟微信专业不在这,是吧!这点也能原谅啦!
注意事项
调用统一支付订单得进行两次签名,第一次是掉起统一订单,获取交易单号用,第二次是将返回的结果加签后返回给小程序端,掉起支付收银台用。
这两次特别要注意以下几点:
第一次加签:
1)参数传入是要按照一定顺序的,复制以上代码就行;
2)必须传入openid,切支付方式要设置为JSAPI。
第二次加签:
这个特别容易出错
1)一定要注意传入参数、传入参数、传入参数,重要事情说三遍,参数采用的驼峰命名,一定要看清楚,不然前台必然报签名错误。
服务端异步支付回调notify_url注意事项
1)http/https协议,亲测都行,端口号不限制,并没有网上说的什么必须http啦,80端口啦之类,只要是外网能访问的url就行。
2)自己写了回调地址,一定外网测试下通不通,不然不通了又得查很久。
3)当然当你试过所有方法仍然不能回调的情况下,那你只能手动掉起交易查询接口了,接口需要验签,验签成功后可以写自己的业务逻辑,安全性完全没有问题哦!
以上都是自己采坑总结,希望对大家有所帮助
需要帮助写支付接口或者要源码的可以联系我,简单的就直接解决了,复杂的还望给点辛苦费啦!哈哈
最后上个客栈地址:
https://www.proginn.com/wo/134620
本人支付宝、银联、微信,各种扫码、app、小程序支付服务端都没有问题。
有需求可以找我哦!欢迎大家叨扰。

467

被折叠的 条评论
为什么被折叠?



