最近写了一个微信小程序的后端,微信支付(Java版)。在这里分享一下开发过程吧。


首先我们要先去了解微信小程序支付开发的过程。

这里说一下流程。

  1. 微信小程序端获取到code,由微信小程序前端传code到后端,
  2. 后端接收到code去调用微信官方接口换取openid。
  3. 生成商户订单,即通过xml格式包含文档中必填的参数去调用统一下单接口(附下图),返回prepay_id.
  4. 当然微信小程序需要二次签名,同样通过必填参数封装,返回给微信小程序端。(其中有很多坑,下文慢慢讲解)

正文开始

1、微信小程序前端获取到code

//获取code的方法wx.login({success: res => {console.log("向后台传递获取用户的信息的地址");console.log(res.code);// 发送 res.code 到后台换取 openId//此处可以请求后台接口获取openid}
})

2.后台接收到code后,通过拼接字符串请求地址,返回openid(在后续附上工具类)

public static String getOpenId(String code) {String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + 你的APP_ID +"&secret=" + 你的APP_SECRET + "&js_code=" + code + "&grant_type=authorization_code";HttpUtil httpUtil = new HttpUtil();try {HttpResult httpResult = httpUtil.doGet(url, null, null);if (httpResult.getStatusCode() == 200) {JsonParser jsonParser = new JsonParser();JsonObject obj = (JsonObject) jsonParser.parse(httpResult.getBody());System.out.println("getOpenId: " + obj.toString());if (obj.get("errcode") != null) {System.out.println("getOpenId returns errcode: " + obj.get("errcode"));return "";} else {return obj.get("openid").toString();}//return httpResult.getBody();}} catch (Exception e) {e.printStackTrace();}return "";}

3.其次是调用统一下单接口,(这一步也是很多人采坑的地方)官方详细文档

  1. 统一下单接口访问的接口为:https://api.mch.weixin.qq.com/pay/unifiedorder
  2. 统一下单接口必填参数为:[共十个]appid(小程序ID),mch_id(商户号),nonce_str(随机字符串),sign(签名),body(商品描述),out_trade_no(商户订单号),total_fee(标价金额),spbill_create_ip(终端ip),notify_url(回调地址),trade_type(交易类型)

**注意:**这里的nonce_str必须与再次签名的随机字符串为同一值。

我这里是通过封装一个实体类,对类进行赋值取值。 (当然,嫌麻烦的可以直接全部写在配置类中)

public class PayInfo implements Serializable {private String appid;//appidprivate String body;//商品描述private String mch_id;//商户号//private String device_info; //设备号,小程序传"WEB"private String nonce_str;//随机字符串private String notify_url;//通知地址private String openid;//用户标识private String out_trade_no;//商户订单号private String sign;//签名private String sign_type;  //签名类型private String spbill_create_ip;//终端ip//private String detail;//商品详情//private String attach;附加数据private String time_expire;//交易结束时间private String time_start;//起始交易时间private int total_fee;//商户金额private String trade_type; //交易类型,JSAPI//private String limit_pay;  //指定支付方式,no_creditprivate String TimeStamp;private String repay_id;//这里应该设置package,但是是关键字。private String key;

…省略get和set…

这一步是 对实体类设置需要的值。(酌情修改参数,不必填的可以注释掉,上面写了必填参数!) 注意:微信官方要求md5加密必须符合字母表顺序,这一步在写实体类的时候要注意(属性名要按字母表写)。

set和get方法不影响。

private PayInfo createPayInfo(String openId, String clientIP, String randomNonceStr) {Date date = new Date();String timeStart = TimeUtils.getFormatTime(date, Constant.TIME_FORMAT);String timeExpire = TimeUtils.getFormatTime(TimeUtils.addDay(date, Constant.TIME_EXPIRE), Constant.TIME_FORMAT);String randomOrderId = CommonUtil.getRandomOrderId();PayInfo payInfo = new PayInfo();payInfo.setAppid(Constant.APP_ID);payInfo.setMch_id(Constant.MCH_ID);payInfo.setDevice_info("WEB");payInfo.setNonce_str(randomNonceStr);payInfo.setSign_type("MD5");  //默认即为MD5payInfo.setBody("JSAPI支付测试");//这里如果是中文需要进行转码,不然可能会报错payInfo.setAttach("支付测试4luluteam");payInfo.setOut_trade_no(randomOrderId);payInfo.setTotal_fee(1);payInfo.setSpbill_create_ip(clientIP);payInfo.setTime_start(timeStart);payInfo.setTime_expire(timeExpire);payInfo.setNotify_url(Constant.URL_NOTIFY);payInfo.setTrade_type("JSAPI");payInfo.setLimit_pay("no_credit");payInfo.setOpenid(openId);return payInfo;

设置好参数,这一步是对字符串进行拼接。目的是md5加密!(酌情删减)

 private String getSign(PayInfo payInfo) throws Exception {StringBuffer sb = new StringBuffer();sb.append("appid=" + payInfo.getAppid()).append("&attach=" + payInfo.getAttach()).append("&body=" + payInfo.getBody()).append("&device_info=" + payInfo.getDevice_info()).append("&limit_pay=" + payInfo.getLimit_pay()).append("&mch_id=" + payInfo.getMch_id()).append("&nonce_str=" + payInfo.getNonce_str()).append("&notify_url=" + payInfo.getNotify_url()).append("&openid=" + payInfo.getOpenid()).append("&out_trade_no=" + payInfo.getOut_trade_no()).append("&sign_type=" + payInfo.getSign_type()).append("&spbill_create_ip=" + payInfo.getSpbill_create_ip()).append("&time_expire=" + payInfo.getTime_expire()).append("&time_start=" + payInfo.getTime_start()).append("&total_fee=" + payInfo.getTotal_fee()).append("&trade_type=" + payInfo.getTrade_type()).append("&key=" + Constant.APP_KEY);log.error("排序后的拼接参数:" + sb.toString());return CommonUtil.getMD5(sb.toString().trim()).toUpperCase();}

这里进行统一下单。返回的是prepay_id这个值。 如果这里报了签名错误等问题。[解决方案]1、仔细检查10个必填参数是否按顺序拼接。2、检查一下appid,mch_id等(或者更改一下key[微信端会抽风])

 /*** 调用统一下单接口* @param openId*/private String unifiedOrder(String openId, String clientIP, String randomNonceStr) {try {String url = Constant.URL_UNIFIED_ORDER;PayInfo payInfo = createPayInfo(openId, clientIP, randomNonceStr);//这里对payInfo对象的九个必须参数进行加密,得到sign值。String md5 = getSign(payInfo);payInfo.setSign(md5);log.error("md5 value: " + md5);String xml = CommonUtil.payInfoToXML(payInfo);xml = xml.replace("__", "_").replace("<![CDATA[1]]>", "1");//xml = xml.replace("__", "_").replace("<![CDATA[", "").replace("]]>", "");log.error(xml);StringBuffer buffer = HttpUtil.httpsRequest(url, "POST", xml);log.error("unifiedOrder request return body: \n" + buffer.toString());Map<String, String> result = CommonUtil.parseXml(buffer.toString());String return_code = result.get("return_code");if(StringUtils.isNotBlank(return_code) && return_code.equals("SUCCESS")) {String return_msg = result.get("return_msg");if(StringUtils.isNotBlank(return_msg) && !return_msg.equals("OK")) {//log.error("统一下单错误!");return "";}String prepay_Id = result.get("prepay_id");return prepay_Id;} else {return "";}} catch (Exception e) {e.printStackTrace();}return "";}

输出结果:

得不到prepay_id,很可能是sign出错了。建议多核对几遍。


我们得到prepay_id之后,将组合数据再次进行签名。

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.company.service.GoodsOrderService;
import com.example.company.utils.HttpClientUtils;
import com.example.company.utils.Pay.*;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.commons.lang3.StringUtils;
import org.jdom.JDOMException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public R prePay(String code, HttpServletRequest request) throws IllegalAccessException {//调用静态方法getOpenIdString openId = getOpenId(code);//获取到openidopenId = openId.replace("\"", "").trim();//这里获取到prepayIdString prepayId = unifiedOrder(openId, clientIP, randomNonceStr);//获取客户端ipString clientIP = CommonUtil.getClientIp(request);//System.out.println("prepayId: " + prepayId);//二次签名加密封装final long time = System.currentTimeMillis() / 1000;String timeStamp = String.valueOf(time);String paySign = "appId=" + Constant.APP_ID + "&nonceStr=" + randomNonceStr + "&package=prepay_id=" + prepayId+ "&signType=MD5" + "&timeStamp=" + timeStamp + "&key="+Constant.APP_KEY;String sign = DigestUtils.md5DigestAsHex(paySign.getBytes(StandardCharsets.UTF_8)).toUpperCase();//System.out.println("sign===="+sign);//再次签名Map map = new HashMap();//map.put("appId", Constant.APP_ID);//获取appidmap.put("nonceStr", randomNonceStr);//返回随机字符串map.put("package", "prepay_id=" + prepayId);//返回prepayIdmap.put("paySign", sign);map.put("signType", "MD5");map.put("timeStamp", timeStamp);//获取时间戳String content = null;try {content = JSON.toJSONString(map);//以json字符串形式返还给调用者System.out.println("content" + content);} catch (Exception e) {e.printStackTrace();}return R.ok().data("content", content);}

得到的"content"的字符串值为:

做到这里,已经几近成功了。还差最后一步,回调函数(当支付成功时,在数据库中修改"已支付"成功字段。)

注意:1、回调地址必须是公网上的地址。(项目部署到服务器上才可进行访问)。 2、回调地址中不能添加任何参数。

public String notifyUrl(HttpServletRequest request, HttpServletResponse response) throws IOException, JDOMException {//System.out.println(request);System.out.println("微信支付回调");//用字节流来获取数据。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");Map<String, String> params = PayCommonUtil.doXMLParse(resultxml);outSteam.close();inStream.close();/**for(Map.Entry<String,String> entry:params.entrySet()){System.out.println(entry.getKey()+" ======[[]]====="+entry.getValue());}*//*** 这里得到的params的字符串对。* transaction_id ======[[]]=====4200000819202011192847521125* nonce_str ======[[]]=====BBJIJ1vrDqxistqhFukLIjuHe7PP21DP* bank_type ======[[]]=====OTHERS* openid ======[[]]=====ozP-C4m-93TNpk0F40yvzJA12wa0* sign ======[[]]=====F90FFB4B9DEA5F7846B28E93209C3A93* fee_type ======[[]]=====CNY* mch_id ======[[]]=====显示自己的商户号* cash_fee ======[[]]=====1* out_trade_no ======[[]]=====20201119165722672* appid ======[[]]=====显示自己的appid* total_fee ======[[]]=====1* trade_type ======[[]]=====JSAPI* result_code ======[[]]=====SUCCESS* time_end ======[[]]=====20201119165807* is_subscribe ======[[]]=====N* return_code ======[[]]=====SUCCESS*/Map<String, String> return_data = new HashMap<String, String>();if (!PayCommonUtil.isTenpaySign(params)) {// 支付失败return_data.put("return_code", "FAIL");return_data.put("return_msg", "return_code不正确");return StringUtil.GetMapToXML(return_data);} else {System.out.println("===============付款成功==============");// ------------------------------// 处理业务开始// ------------------------------// 此处处理订单状态,结合自己的订单数据完成订单状态的更新,修改订单状态等。// ------------------------------String out_trade_no = String.valueOf(Long.parseLong(params.get("out_trade_no").split("O")[0]));System.out.println("out_trade_no==="+out_trade_no);Date accountTime = DateUtil.stringtoDate(params.get("time_end"), "yyyyMMddHHmmss");System.out.println("accountTime==="+accountTime);String ordertime = DateUtil.dateToString(new Date(), "yyyy-MM-dd HH:mm:ss");System.out.println("ordertime==="+ordertime);String totalAmount = String.valueOf(v);System.out.println("totalAmount==="+totalAmount);String appId = params.get("appid");System.out.println("appId==="+appId);String tradeNo = params.get("transaction_id");System.out.println("tradeNo==="+tradeNo);return_data.put("return_code", "SUCCESS");return_data.put("return_msg", "OK");return StringUtil.GetMapToXML(return_data);}}

附上工具类。

public class Constant {//public static final String DOMAIN = "https://i-test.com.cn";public static final String APP_ID = "你的id";//小程序IDpublic static final String APP_SECRET = "你的secret";public static final String APP_KEY = "商户key";public static final String MCH_ID = "你的商户号";  //商户号public static final String URL_UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";public static final String URL_CHECK_ORDER = "https://api.mch.weixin.qq.com/pay/orderquery";public static final String URL_NOTIFY = "你的公网地址";//回调 地址public static final String TIME_FORMAT = "yyyyMMddHHmmss";public static final int TIME_EXPIRE = 2;  //单位是day}public class PayCommonUtil {/*** 验证回调签名* @return*/public static boolean isTenpaySign(Map<String, String> map) {String characterEncoding="utf-8";String charset = "utf-8";String signFromAPIResponse = map.get("sign");if (signFromAPIResponse == null || signFromAPIResponse.equals("")) {System.out.println("API返回的数据签名数据不存在,有可能被第三方篡改!!!");return false;}System.out.println("服务器回包里面的签名是:" + signFromAPIResponse);//过滤空 设置 TreeMapSortedMap<String,String> packageParams = new TreeMap();for (String parameter : map.keySet()) {String parameterValue = map.get(parameter);String v = "";if (null != parameterValue) {v = parameterValue.trim();}packageParams.put(parameter, v);}StringBuffer sb = new StringBuffer();Set es = packageParams.entrySet();Iterator it = es.iterator();while(it.hasNext()) {Map.Entry entry = (Map.Entry)it.next();String k = (String)entry.getKey();String v = (String)entry.getValue();if(!"sign".equals(k) && null != v && !"".equals(v)) {sb.append(k + "=" + v + "&");}}sb.append("key=" + API_KEY);//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较//算出签名String resultSign = "";String tobesign = sb.toString();if (null == charset || "".equals(charset)) {resultSign = MD5Util.MD5Encode(tobesign, characterEncoding).toUpperCase();}else{try{resultSign = MD5Util.MD5Encode(tobesign, characterEncoding).toUpperCase();}catch (Exception e) {resultSign = MD5Util.MD5Encode(tobesign, characterEncoding).toUpperCase();}}String tenpaySign = ((String)packageParams.get("sign")).toUpperCase();return tenpaySign.equals(resultSign);}
}import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;import javax.servlet.http.HttpServletRequest;
import java.io.Writer;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.*;
public class CommonUtil {public static String getRandomOrderId() {// UUID.randomUUID().toString().replace("-","")Random random = new Random(System.currentTimeMillis());int value = random.nextInt();while (value < 0) {value = random.nextInt();}return value + "";}private static XStream xstream = new XStream(new XppDriver() {public HierarchicalStreamWriter createWriter(Writer out) {return new PrettyPrintWriter(out) {//增加CDATA标记boolean cdata = true;@SuppressWarnings("rawtypes")public void startNode(String name, Class clazz) {super.startNode(name, clazz);}protected void writeText(QuickWriter writer, String text) {if (cdata) {writer.write("<![CDATA[");writer.write(text);writer.write("]]>");} else {writer.write(text);}}};}});public static String payInfoToXML(PayInfo pi) {xstream.alias("xml", pi.getClass());return xstream.toXML(pi);}@SuppressWarnings("unchecked")public static Map<String, String> parseXml(String xml) throws Exception {Map<String, String> map = new HashMap<>();Document document = DocumentHelper.parseText(xml);Element root = document.getRootElement();List<Element> elementList = root.elements();for (Element e : elementList)map.put(e.getName(), e.getText());return map;}public static String getClientIp(HttpServletRequest request) {String ip = request.getHeader("X-Forwarded-For");if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){//多次反向代理后会有多个ip值,第一个ip才是真实ipint index = ip.indexOf(",");if(index != -1){return ip.substring(0,index);}else{return ip;}}ip = request.getHeader("X-Real-IP");if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){return ip;}return request.getRemoteAddr();}/*** 对字符串md5加密** @param str* @return*/public static String getMD5(String str) throws Exception {try {// 生成一个MD5加密计算摘要MessageDigest md = MessageDigest.getInstance("MD5");// 计算md5函数md.update(str.getBytes());// digest()最后确定返回md5 hash值,返回值为8为字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符// BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值return new BigInteger(1, md.digest()).toString(16);} catch (Exception e) {throw new Exception("MD5加密出现错误");}}}import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
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.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;public class HttpUtil {// User-Agentpublic static final String USERAGENT_FIREFOX = "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0";  public static final String USERAGENT_IE = "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko";  private CloseableHttpClient httpClient;private BasicCookieStore cookieStore;private HttpGet get;private HttpPost post;public static StringBuffer httpsRequest(String requestUrl, String requestMethod, String output) throws IOException {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;}public HttpResult doGet(String url, Map<String, String> headers, Map<String, String> params) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException, ClientProtocolException, IOException {if (url == null|| url.equals("")) {return null;}SSLContextBuilder builder = new SSLContextBuilder();builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());cookieStore = new BasicCookieStore();CloseableHttpClient httpclient = HttpClients.custom().setDefaultCookieStore(cookieStore).setSSLSocketFactory(sslsf).build();HttpResult result = null;try {url = url + "?" + parseParams(params);HttpGet httpget = new HttpGet(url);httpget.setHeaders(parseHeader(headers));CloseableHttpResponse response = httpclient.execute(httpget);try {HttpEntity entity = response.getEntity();if (entity != null) {result = new HttpResult();result.setCookies(cookieStore.getCookies());result.setStatusCode(response.getStatusLine().getStatusCode());result.setHeaders(response.getAllHeaders());result.setBody(EntityUtils.toString(entity));}} finally {response.close();}} finally {httpclient.close();}return result;}public HttpResult doPost(String url, Map<String, String> headers, Map<String, String> postData, String encoding) throws Exception {if (url == null|| url.equals("")) {return null;}if (encoding == null|| encoding.equals("")) {encoding = "utf-8";}SSLContextBuilder builder = new SSLContextBuilder();builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());cookieStore = new BasicCookieStore();CloseableHttpClient httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore).setSSLSocketFactory(sslsf).build();post = new HttpPost(url);List<NameValuePair> list = new ArrayList<NameValuePair>();for (String tmp : postData.keySet()) {list.add(new BasicNameValuePair(tmp, postData.get(tmp)));}post.setEntity(new UrlEncodedFormEntity(list, encoding));post.setHeaders(parseHeader(headers));CloseableHttpResponse response = httpClient.execute(post);HttpEntity entity = response.getEntity();HttpResult result = new HttpResult();result.setCookies(cookieStore.getCookies());result.setStatusCode(response.getStatusLine().getStatusCode());result.setHeaders(response.getAllHeaders());result.setBody(EntityUtils.toString(entity, encoding));close(entity, response);return result;}
}

在这里做个小总结吧。

微信官方的文档意思就是。

第一次签名:appid,mch_id,nonce_str,body,out_trade_no,total_fee,spbill_create_ip,notify_url,trade_type

这九个参数进行md5加密,为sign,然后九个参数+sign以xml的格式发送请求给微信统一下单API返回的xml格式中有数据。

第二次签名:appId,nonceStr,randomNonceStr,package,signType,timeStamp,key[七个参数]进行拼接签名。

String paySign = “appId=” + Constant.APP_ID + “&nonceStr=” +
randomNonceStr + “&package=prepay_id=” + prepayId + “&signType=MD5” +
“&timeStamp=” + timeStamp + “&key=”+Constant.APP_KEY;

然后paySign拼接之后进行md5加密。

最后返回

("nonceStr", randomNonceStr);//返回随机字符串<br>
("package", "prepay_id=" + prepayId);//返回prepayId<br>
("paySign", sign);<br>
("signType", "MD5");<br>
("timeStamp", timeStamp);//获取时间戳<br>

结束

好了。我这里是只做了微信小程序的支付及回调。

微信小程序-微信支付相关推荐

  1. 微信小程序篇(微信小程序的支付)

    微信小程序的支付和微信公众号的支付是类似的,对比起来还比公众号支付简单了一些,我们只需要调用微信的统一下单接口获取prepay_id之后我们在调用微信的支付即可. 今天我们来封装一般node的支付接口 ...

  2. 微信小程序服务器支付sdk,微信小程序之支付后如何调用SDK的异步通知

    微信小程序之支付后如何调用SDK的异步通知 发布时间:2021-07-05 10:47:33 来源:亿速云 阅读:57 作者:小新 这篇文章主要介绍微信小程序之支付后如何调用SDK的异步通知,文中介绍 ...

  3. 微信小程序调用支付接口支付(tp5、小程序)

    微信小程序调用支付接口支付 今天记录一下学习的小程序调用微信支付接口 一.先理清一下调起微信支付的整个流程. 1.就是先调用微信的支付统一下单api获取到prepay_id 2.然后后端再将这个pre ...

  4. ios微信小程序虚拟支付解决办法

    ios微信小程序虚拟支付整理介绍 目前iOS端暂不支持虚拟支付,微信小程序虚拟支付仅涉及到ios端,安卓端不受影响. 小程序支付规范 https://developers.weixin.qq.com/ ...

  5. 微信小程序 微信支付代码实现流程

    微信小程序 微信支付是一个很简单的流程  微信开发文档 地址:wx.requestPayment(Object object) | 微信开放文档 微信公众平台申请支付功能 百度一大堆例举代码 官方文档 ...

  6. 微信小程序—微信小程序端支付代码

    只有微信小程序端的代码,如下 Page({data: {},onLoad: function (options) {// 页面初始化 options为页面跳转所带来的参数var that = this ...

  7. 微信小程序微信支付《JSAPI支付》APIV3详细教程

    文章目录 前提 整体介绍 我的maven依赖 1.整体流程 2.openid 的获取 3.统一下单Controller(预支付订单) 4.配置类和配置文件 5.工具类 6.前端接收到必要的参数,进行调 ...

  8. 微信小程序-微信支付退款

    微信小程序-微信支付退款 官方接口文档及相关附件 申请退款 SDK 错误集锦 调用该https://api.mch.weixin.qq.com/secapi/pay/refund接口需要应程序安装AP ...

  9. 微信小程序支付返回签名错误_java 微信小程序微信支付统一下订单及数字签名错误问题(后端)...

    今天来分享一下之前做微信小程序微信支付遇到的一些坑,博主这里是微信小程序支付功能,因此选择的微信支付方式是JSAPI支付方式(温馨提示左下角有音乐哦). 首先我们肯定是要在小程序后台绑定一个商户号的, ...

  10. 微信小程序配置支付(附完整代码)

    微信小程序配置支付 1. 微信支付官网配置项 2. 代码配置(登录.获取openId.拉起支付等) 3. 云开发示例 1. 微信支付官网配置项 初次接入,商户号管理是没有数据的,需要申请接入. 如果是 ...

最新文章

  1. Swift类与OC类方法相互调用的
  2. TabControl控件和TabPage
  3. 最长回文子串动态规划_九章算法 | 微软面试题:最长回文子串
  4. 消防信号总线原理_消防报警系统中消防模块分类与用途简介
  5. java切面获取reqiest,怎么在webflux切面中获取请求头.
  6. Android插件化开发之动态加载技术简单易懂的介绍方式
  7. 转码java,java转码
  8. 容器编排技术 -- 使用Minikube 部署 Kubernetes 集群
  9. matlab lti全响应,《LTI系统的响应——实验报告》.doc
  10. 使用J-flash arm下载程序
  11. ubuntu下安装win7虚拟机总结
  12. 数学建模算法体系分类
  13. 如何做蛋白质互作网络图
  14. 《Spring Cloud微服务架构实战派》PDF样章
  15. idea切换分支忘记commit,导致自己辛苦写的代码被覆盖,找不回来,别慌,这里教你一个好办法找回你未提交的代码
  16. 程序员C语言快速上手——进阶篇(八)
  17. (兔C残篇)关于python 列表的介绍
  18. 基于改进FCOS的钢带表面缺陷检测算法
  19. 517我要吃:20大城市的20道特色面食与特色美食小吃
  20. el-tree中实现拖拽遇到的问题

热门文章

  1. navicat 1146错误
  2. elementui 做删除,分页页码不正确
  3. JPEG图像的可逆信息隐藏算法研究
  4. 解除excel打开密码
  5. Excel 工作表密码破解
  6. OPENSSH格式的private key转换成RSA格式生成pem文件
  7. 简单好用的洗鼻子方法?
  8. BGP 路由聚合(Huawei设备)
  9. 在线抢购平台_课程设计报告
  10. 产品经理的自我修养—认知模式