JAVA微信扫码支付模式二线上支付功能实现以及回调

 一、准备工作

首先吐槽一下微信关于支付这块,本身支持的支付模式就好几种,但是官方文档特别零散,连像样的Java相关的demo也没几个。本人之前没有搞过微信支付,一开始真是被它搞晕,折腾两天终于调通了,特此写下来,以享后人吧!

关于准备工作,就“微信扫码支付模式二”官方文档地址在这 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1 可以先看看,实际上需要准备的东西有以下几个:

其中APP_ID和APP_SECRET可以在公众平台找着,MCH_ID和API_KEY则在商户平台找到,特别是API_KEY要在商户平台设置好,对于“微信扫码支付模式二”(支付与回调)实际只会用到APP_ID、MCH_ID和API_KEY,其他的都不用。

关于开发环境,我就不罗嗦了,不管你是springMVC、struts2又或者直接serverlet,都差不多,只要你能保证对应的方法能调用起来就行。关于引用第三方的jar包,我这里只用到了一个xml操作的jdom,记住是1.*的版本,不是官网上最新的2.*,两者不兼容。具体是jdom-1.1.3.jar,依赖包jaxen-1.1.6.jar,就这两个包,我没用到有些例子中使用的httpclient,感觉没必要,而且依赖包特别繁杂,当然你是maven当我没说。

二、开发实战

1、首先是接入微信接口,获取微信支付二维码。

public String weixin_pay() throws Exception {
    // 账号信息
    String appid = PayConfigUtil.APP_ID; // appid
    //String appsecret = PayConfigUtil.APP_SECRET; // appsecret
    String mch_id = PayConfigUtil.MCH_ID; // 商业号
    String key = PayConfigUtil.API_KEY; // key 

    String currTime = PayCommonUtil.getCurrTime();
    String strTime = currTime.substring(8, currTime.length());
    String strRandom = PayCommonUtil.buildRandom(4) + "";
    String nonce_str = strTime + strRandom; 

    String order_price = 1; // 价格  注意:价格的单位是分
    String body = "goodssssss";  // 商品名称
    String out_trade_no = "11338"; // 订单号 

    // 获取发起电脑 ip
    String spbill_create_ip = PayConfigUtil.CREATE_IP;
    // 回调接口
    String notify_url = PayConfigUtil.NOTIFY_URL;
    String trade_type = "NATIVE"; 

    SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
    packageParams.put("appid", appid);
    packageParams.put("mch_id", mch_id);
    packageParams.put("nonce_str", nonce_str);
    packageParams.put("body", body);
    packageParams.put("out_trade_no", out_trade_no);
    packageParams.put("total_fee", order_price);
    packageParams.put("spbill_create_ip", spbill_create_ip);
    packageParams.put("notify_url", notify_url);
    packageParams.put("trade_type", trade_type); 

    String sign = PayCommonUtil.createSign("UTF-8", packageParams,key);
    packageParams.put("sign", sign); 

    String requestXML = PayCommonUtil.getRequestXml(packageParams);
    System.out.println(requestXML); 

    String resXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML); 

    Map map = XMLUtil.doXMLParse(resXml);
    //String return_code = (String) map.get("return_code");
    //String prepay_id = (String) map.get("prepay_id");
    String urlCode = (String) map.get("code_url"); 

    return urlCode;
}

如果不出意外的话,这里就从微信服务器获取了一个支付url,形如weixin://wxpay/bizpayurl?pr=pIxXXXX,之后我们就需要把这个url生成一个二维码,然后就可以使用自己手机微信端扫码支付了。关于二维码生成有很多种方法,各位各取所需吧,我这里提供一个google的二维码生成接口:

public static String QRfromGoogle(String chl) throws Exception {
  int widhtHeight = 300;
  String EC_level = "L";
  int margin = 0;
  chl = UrlEncode(chl);
  String QRfromGoogle = "http://chart.apis.google.com/chart?chs=" + widhtHeight + "x" + widhtHeight
      + "&cht=qr&chld=" + EC_level + "|" + margin + "&chl=" + chl; 

  return QRfromGoogle;
} 
// 特殊字符处理
public static String UrlEncode(String src) throws UnsupportedEncodingException {
  return URLEncoder.encode(src, "UTF-8").replace("+", "%20");
} 

上面代码中涉及到几个工具类:PayConfigUtil、PayCommonUtil、HttpUtil和XMLUtil,其中PayConfigUtil放的就是上面提到一些配置及路径,PayCommonUtil涉及到了获取当前事件、产生随机字符串、获取参数签名和拼接xml几个方法,代码如下:

public class PayCommonUtil { 

  /**
   * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
   * @return boolean
   */
  public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
    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); 

    //算出摘要
    String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
    String tenpaySign = ((String)packageParams.get("sign")).toLowerCase(); 

    //System.out.println(tenpaySign + "  " + mysign);
    return tenpaySign.equals(mysign);
  } 

  /**
   * @author
   * @date 2016-4-22
   * @Description:sign签名
   * @param characterEncoding
   *      编码格式
   * @param parameters
   *      请求参数
   * @return
   */
  public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
    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 (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
        sb.append(k + "=" + v + "&");
      }
    }
    sb.append("key=" + API_KEY);
    String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
    return sign;
  } 

  /**
   * @author
   * @date 2016-4-22
   * @Description:将请求参数转换为xml格式的string
   * @param parameters
   *      请求参数
   * @return
   */
  public static String getRequestXml(SortedMap<Object, 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 k = (String) entry.getKey();
      String v = (String) entry.getValue();
      if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
        sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
      } else {
        sb.append("<" + k + ">" + v + "</" + k + ">");
      }
    }
    sb.append("</xml>");
    return sb.toString();
  } 

  /**
   * 取出一个指定长度大小的随机正整数.
   *
   * @param length
   *      int 设定所取出随机数的长度。length小于11
   * @return int 返回生成的随机数。
   */
  public static int buildRandom(int length) {
    int num = 1;
    double random = Math.random();
    if (random < 0.1) {
      random = random + 0.1;
    }
    for (int i = 0; i < length; i++) {
      num = num * 10;
    }
    return (int) ((random * num));
  } 

  /**
   * 获取当前时间 yyyyMMddHHmmss
   *
   * @return String
   */
  public static String getCurrTime() {
    Date now = new Date();
    SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
    String s = outFormat.format(now);
    return s;
  } 

}

HttpUtil和XMLUtil如下:

public class HttpUtil { 

  private static final Log logger = Logs.get();
  private final static int CONNECT_TIMEOUT = 5000; // in milliseconds
  private final static String DEFAULT_ENCODING = "UTF-8"; 

  public static String postData(String urlStr, String data){
    return postData(urlStr, data, null);
  } 

  public static String postData(String urlStr, String data, String contentType){
    BufferedReader reader = null;
    try {
      URL url = new URL(urlStr);
      URLConnection conn = url.openConnection();
      conn.setDoOutput(true);
      conn.setConnectTimeout(CONNECT_TIMEOUT);
      conn.setReadTimeout(CONNECT_TIMEOUT);
      if(contentType != null)
        conn.setRequestProperty("content-type", contentType);
      OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);
      if(data == null)
        data = "";
      writer.write(data);
      writer.flush();
      writer.close();  

      reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));
      StringBuilder sb = new StringBuilder();
      String line = null;
      while ((line = reader.readLine()) != null) {
        sb.append(line);
        sb.append("\r\n");
      }
      return sb.toString();
    } catch (IOException e) {
      logger.error("Error connecting to " + urlStr + ": " + e.getMessage());
    } finally {
      try {
        if (reader != null)
          reader.close();
      } catch (IOException e) {
      }
    }
    return null;
  }
}
public class XMLUtil {
  /**
   * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
   * @param strxml
   * @return
   * @throws JDOMException
   * @throws IOException
   */
  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 = XMLUtil.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(XMLUtil.getChildrenText(list));
        }
        sb.append(value);
        sb.append("</" + name + ">");
      }
    } 

    return sb.toString();
  } 

}

当然还有一个MD5计算工具类

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" }; 

}

2、支付回调

支付完成后,微信会把相关支付结果和用户信息发送到我们上面指定的那个回调地址,我们需要接收处理,并返回应答。对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)

关于支付回调接口,我们首先要对于支付结果通知的内容进行签名验证,然后根据支付结果进行相应的处理流程即可。

public void weixin_notify(HttpServletRequest request,HttpServletResponse response) throws Exception{ 

    //读取参数
    InputStream inputStream ;
    StringBuffer sb = new StringBuffer();
    inputStream = request.getInputStream();
    String s ;
    BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
    while ((s = in.readLine()) != null){
      sb.append(s);
    }
    in.close();
    inputStream.close(); 

    //解析xml成map
    Map<String, String> m = new HashMap<String, String>();
    m = XMLUtil.doXMLParse(sb.toString()); 

    //过滤空 设置 TreeMap
    SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
    Iterator it = m.keySet().iterator();
    while (it.hasNext()) {
      String parameter = (String) it.next();
      String parameterValue = m.get(parameter); 

      String v = "";
      if(null != parameterValue) {
        v = parameterValue.trim();
      }
      packageParams.put(parameter, v);
    } 

    // 账号信息
    String key = PayConfigUtil.API_KEY; // key 

    logger.info(packageParams);
    //判断签名是否正确
    if(PayCommonUtil.isTenpaySign("UTF-8", packageParams,key)) {
      //------------------------------
      //处理业务开始
      //------------------------------
      String resXml = "";
      if("SUCCESS".equals((String)packageParams.get("result_code"))){
        // 这里是支付成功
        //////////执行自己的业务逻辑////////////////
        String mch_id = (String)packageParams.get("mch_id");
        String openid = (String)packageParams.get("openid");
        String is_subscribe = (String)packageParams.get("is_subscribe");
        String out_trade_no = (String)packageParams.get("out_trade_no"); 

        String total_fee = (String)packageParams.get("total_fee"); 

        logger.info("mch_id:"+mch_id);
        logger.info("openid:"+openid);
        logger.info("is_subscribe:"+is_subscribe);
        logger.info("out_trade_no:"+out_trade_no);
        logger.info("total_fee:"+total_fee); 

        //////////执行自己的业务逻辑//////////////// 

        logger.info("支付成功");
        //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
        resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
            + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; 

      } else {
        logger.info("支付失败,错误信息:" + packageParams.get("err_code"));
        resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
            + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
      }
      //------------------------------
      //处理业务完毕
      //------------------------------
      BufferedOutputStream out = new BufferedOutputStream(
          response.getOutputStream());
      out.write(resXml.getBytes());
      out.flush();
      out.close();
    } else{
      logger.info("通知签名验证失败");
    } 

  }

签名验证算法和签名生成的算法类似,在上面PayCommonUtil工具类中提供。
三、后话

感觉微信扫描支付体验效果还是挺好的,唯一缺点就是相关文档零散,官方的demo居然没有java编写的,希望之后微信官方能够逐步完善吧!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java编程接口回调一般用法代码解析

    接口回调是指:可以把使用某一接口的类创建的对象的引用赋给该接口声明的接口变量,那么该接口变量就可以调用被类实现的接口的方法.实际上,当接口变量调用被类实现的接口中的方法时,就是通知相应的对象调用接口的方法,这一过程称为对象功能的接口回调. Java接口回调一般用法:实现接口实际上和继承抽象类类似,只不过继承是在类的层面上操作,接口是在方法和常量集合的层面上操作,接口比抽象类更抽象.更简洁.可以把实现接口看成继承特定的一个或多个方法以及一些常量,关于接口的具体规则这里不赘述. 为什么要使用接口和抽

  • 详解Java回调的原理与实现

    回调原本应该是一个非常简单的概念,但是可能因为平时只用系统为我们写好的回调的接口了,自己很少实现回调,所以在自己实现回调的时候还是有一点点晕的,现在写这篇文章记录一下,也和大家分享一下怎么写回调接口. 回调 回调的概念:举个例子就是,我们想要问别人一道题,我们把题跟对方说了一下,对方说好,等我做完这道题,我就告诉你,这个时候就用到了回调,因为我们并不知道对方什么时候会做完,而是对方做完了来主动找我们. 同步回调 代码运行到某一个位置的时候,如果遇到了需要回调的代码,会在这里等待,等待回调结果返回

  • Java 回调函数详解及使用

    Java 回调函数详解 前言: C语言中回调函数解释: 回调函数(Callback Function)是怎样一种函数呢? 函数是用来被调用的,我们调用函数的方法有两种: 直接调用:在函数A的函数体里通过书写函数B的函数名来调用之,使内存中对应函数B的代码得以执行.这里,A称为"主叫函数"(Caller),B称为"被叫函数"(Callee). 间接调用:在函数A的函数体里并不出现函数B的函数名,而是使用指向函数B的函数指针p来使内存中属于函数B的代码片断得以执行--听

  • java 接口回调实例详解

    java 接口回调实例详解 首先官方对接口回调的定义是这样的,所谓回调:就是A类中调用B类中的某个方法C,然后B类中反过来调用A类中的方法D,D这个方法就叫回调方法.这样听起来有点绕,我们可以这么理解接口回调:比如我们想知道隔壁老王啥时候回家?但是我们有自己的事情做不能一直监视着老王,那么我们可以雇员小区的保安来完成这个任务,当老王回家口,保安就给我们打电话告诉我们,老王回来了!这样就完成了一个事件的传递: 首先我们定义了一个接口: public interface DynamicMessage

  • Java通过匿名类来实现回调函数实例总结

    在C语言中,函数名可以当做函数指针传递给形参从而实现回调 void f1() { printf("f1()\n"); } void f2() { printf("f2()\n"); } void f3() { printf("f3()\n"); } void do_func(void(*f)()) { f(); } int main() { do_func(f1); do_func(f2); do_func(f3); } 在C++11中,实现回调

  • Java回调方法详解

    回调在维基百科中定义为: 在计算机程序设计中,回调函数,是指通过函数参数传递到其他代码的,某一块可执行代码的引用. 其目的是允许底层代码调用在高层定义的子程序. 举个例子可能更明白一些:以Android中用retrofit进行网络请求为例,这个是异步回调的一个例子. 在发起网络请求之后,app可以继续其他事情,网络请求的结果一般是通过onResponse与onFailure这两个方法返回得到.看一下相关部分的代码: call.enqueue(new Callback<HistoryBean>(

  • JAVA微信扫码支付模式二线上支付功能实现以及回调

     一.准备工作 首先吐槽一下微信关于支付这块,本身支持的支付模式就好几种,但是官方文档特别零散,连像样的Java相关的demo也没几个.本人之前没有搞过微信支付,一开始真是被它搞晕,折腾两天终于调通了,特此写下来,以享后人吧! 关于准备工作,就"微信扫码支付模式二"官方文档地址在这 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1 可以先看看,实际上需要准备的东西有以下几个: 其中APP_ID和APP_SECRE

  • JAVA微信扫码支付模式一线下支付功能实现

    一.准备工作 无数人来追问模式一的开发,所以在这就贴出来,仅供参考.关于模式一和模式二的区别,我有解释过很多次,无非就是模式一的二维码是针对商品的,模式二的二维码是针对订单的,其他具体细节我就不费口舌了,各位可以自行去官方查看文档,然后是选模式一还是模式二就得看自己的业务了. 1.1.有关配置参数 还是之前那四样,APP_ID和APP_SECRET可以在公众平台找着,MCH_ID和API_KEY则在商户平台找到,特别是API_KEY要在商户平台设置好,这个东东关系到参数校验的正确与否,所以一定要

  • 教你用Java在个人电脑上实现微信扫码支付

    Java实现PC微信扫码支付 做一个电商网站支付功能必不可少,那我们今天就来盘一盘微信支付. 微信支付官方网站 业务流程: 开发指引文档 支付服务开发前提准备: 1.SDK下载:SDK 2.利用外网穿透,获得一个外网域名:natapp 3.APPID,商户ID,密钥 注:上面三个参数需要自己申请 开发阶段: 导入依赖: <!--eureka的客户端依赖--> <dependency> <groupId>org.springframework.cloud</grou

  • Java实现微信扫码登入的实例代码

    微信扫码登入 首先去通过微信开放平台做好开发者资质认证,创建网站应用然后等待审核 开发者资质认证 网站应用 审核通过的话就是这个样子 还有最底下的授权回调地址 (www.xxxxx.com) 填写域名即可 pom <!-- WeChatQrCode --> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId&

  • Java SpringMVC实现PC端网页微信扫码支付(完整版)

    一:前期微信支付扫盲知识 前提条件是已经有申请了微信支付功能的公众号,然后我们需要得到公众号APPID和微信商户号,这个分别在微信公众号和微信支付商家平台上面可以发现.其实在你申请成功支付功能之后,微信会通过邮件把Mail转给你的,有了这些信息之后,我们就可以去微信支付服务支持页面:https://pay.weixin.qq.com/service_provider/index.shtml 打开这个页面,点击右上方的链接[开发文档]会进入到API文档说明页面,看起来如下 选择红色圆圈的扫码支付就

  • java实现微信扫码支付功能

    本文实例为大家分享了java实现微信扫码支付的具体代码,供大家参考,具体内容如下 1.maven项目的pom.xml中添加如下jar包: <dependency> <groupId>com.github.wxpay</groupId> <artifactId>wxpay-sdk</artifactId> <version>0.0.3</version> </dependency> 2.编写WeWxConfig类

  • java实现网站微信扫码支付

    一.网站微信扫码支付开发并没有现成的java示例,总结一下自己微信扫码支付心得 二.首先去微信公众平台申请账户 https://mp.weixin.qq.com ** 三.账户开通.开发者认证之后就可以进行微信支付开发了 1.微信统一下单接口调用获取预支付id,以及生成二维码所需的codeUrl /** * 保存订单,并生成二维码所需的codeUrl * * @param request * @param response * @param notifyURLBuf * @param order

  • .NET微信扫码支付接入(模式二-NATIVE)

    一.前言 经过两三天的琢磨总算完成了微信扫码支付功能,不得不感叹几句: 微信提供的DEMO不错,直接复制粘贴就可以跑起来了: 微信的配置平台我真是服了.公众平台.商户平台.开放平台,一个平台一套账户密码,大写的恶心        DEMO地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1 .NET版DEMO中的Lib文件夹是关键,直接复制到自己的代码里,或者打成dll随个人意愿. 二.正文 Step1:肯定是产生商户

  • 微信公众平台开发教程⑤ 微信扫码支付模式介绍

    本文实例讲述了微信扫码支付模式.分享给大家供大家参考,具体如下: 背景:因为微信占据众多的用户群,作为程序开发,自然而然也成了研究的重点.毕竟个人能力有限,很难想象设计的复杂性,多数时间接触起来,各种蒙圈,在此笔记自己的操作流程,仅做参考,欢迎指正. 一.微信扫码支付模式 1.附带微信公众号"微信开发"中,对微信扫码支付的两种模式流程图以作"膜拜". 2.具体的操作,可详细参考官方开发文档 文档有强调: 模式一开发前,商户必须在公众平台后台设置支付回调URL.URL

  • 分享微信扫码支付开发遇到问题及解决方案-附Ecshop微信支付插件

    最近比较工作比较轻松,帮一个朋友的基于ecshop开发的商城加入微信扫描支付功能,本以为是很简单的事儿--下载官方sdk或开发帮助文档,按着里面的做就ok了,谁知折腾了两三天的时间才算搞定,中间也带着疑问在网上找了不少技术文章,却发现都只是比较粗略的写他们是怎么开发接入的,并没有解决我遇到的问题...,唉,有时候真心的感觉'只能靠自己'. 本文就是想把自己遇到的问题及解决办法写出来,让做这方面开发的朋友有所帮助! 开发之前,先查看官方[扫码支付]开发文档,扫码支付分为以下两种模式: △模式一:

随机推荐