微信公众号JSAPI支付详细开发过程

简介:这几天公司有个活动需要用到微信公众号支付,最后成功了完成支付。但第一次使用微信的支付中间遇到一些问题方便以后参考,下面总结整个调用的过程。

  • 开发环境:java
  • 开发系统:win10
  • 开发工具:idea2018
  • 2018-11-26

在开发之前我们要准备以下资料:

  • 开发者ID(AppID) 这个资料在微信公众号开发配置里查看
  • (AppSecret) 这个资料在微信公众号开发配置里查看
  • 接口权限网页服务 网页白名单(这个地方要备案域名)
    支付界面设置这里要开通 JSAPI支付 -https://pay.weixin.qq.com
    产品中心
    -配置好授权的支付页面(此处主要是支付的回调和页面授权)

下面是操作环节和代码code核心部分

  1. 统一下单
    接口地址 URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
字段名 变量名 必填 类型 描述
公众账号ID appid String(21) 这里就是你公众号appid
商户号 mch_id String(32) 这个是支付商户号
随机字符串 nonce_str String(32) 这可以用自带的例子里面生成
签名 sign String(32) 详见签名生成算法
商品描述 body String(128) 这是简单的描述支持中文
商户订单号 out_trade_no String(32) 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-* 且在同一个商户号下唯一
标价金额 total_fee Int 订单总金额,单位为分
终端IP spbill_create_ip String(16) 提交的IP123.12.12.123
通知地址 notify_url String(256) http://www.weixin.qq.com/wxpay/pay.php这个后面通知用的
交易类型 trade_type String(16) JSAPI JSAPI支付 NATIVE Native支付 APP APP支付https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_2

以上参数是必填项目
从上面的格式我们看到很多东西都是自己定义的,唯一的一个东西就是 签名
设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
也就是说 上面你要发送的这些信息都要拼接成一个字符串
比如你要发送这些

mch_id: 10000100 
device_info: 1000 
body: test 
nonce_str: ibuaiVcKdpRxkhJA 

最后拼接后的格式是

string A="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA"; 

按照参数名ASCII码从小到大排序(字典序) 使用下面的函数拼接比较方便

    public static String formatUrlMap(Map<String, String> paramsMap, boolean urlEncode, boolean keyToLower) {

        String buff = "";
        Map<String, String> tmpMap = paramsMap;

        try {
            List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(tmpMap.entrySet());

            //对所有传入参数按照字段名的ASCII码从小到大排序(字典序)
            Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
                public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
                    return (o1.getKey()).toString().compareTo(o2.getKey());
                }
            });

            //构造URL 键值对的格式
            StringBuffer buf = new StringBuffer();
            for (Map.Entry<String, String> item : infoIds) {
                if (StringUtils.isNotBlank(item.getKey())) {
                    String key = item.getKey();
                    String value = item.getValue();
                    if (urlEncode) {
                        value = URLEncoder.encode(value, "utf-8");
                    }
                    if (keyToLower) {
                        buf.append(key.toLowerCase() + "=" + value);
                    } else {
                        buf.append(key + "=" + value);
                    }
                    buf.append("&");
                }
            }
            buff = buf.toString();

            if (StringUtils.isNotEmpty(buff)) {
                buff = buff.substring(0, buff.length() - 1);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        return buff;
    }

第二步 key 在商户注册里面设置 https://pay.weixin.qq.com

stringSignTemp=stringA+"&key=192006250b4c09247ec02edce69f6a2d" //注:key为商户平台设置的密钥key 

第三步:拼接API密钥: 有二个方式 我用的是md5

string SignTemp=A+"&key=192006250b4c09247ec02edce69f6a2d" //注:key为商户平台设置的密钥key 

sign=MD5(SignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7" //注:MD5签名方式

sign=hash_hmac("sha256",SignTemp,key).toUpperCase()="6A9AE1657590FD6257D693A078E1C3E4BB6BA4DC30B23E0EE2496E54170DACD6" //注:HMAC-SHA256签名方式

这里注意都要转成大写

到这里第一大步骤就完成了。可以post数据了,如果post成功会得到类似下面的数据

<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg><appid><![CDATA[wx**********7c26]]></appid><mch_id><![CDATA[14******02]]></mch_id><device_info><![CDATA[WEB]]></device_info><nonce_str><![CDATA[rwo6Cw6mCS3tVeBi]]></nonce_str><sign><![CDATA[CF0D8*******8F56AE8401433]]></sign><result_code><![CDATA[SUCCESS]]></result_code><prepay_id><![CDATA[wx26125**********b3701824863]]></prepay_id><trade_type><![CDATA[JSAPI]]></trade_type></xml>

这里最主要的是 prepay_id=wx26125**********b3701824863 这样的一个类型
然后二次签名
参数需要重新进行签名计算,参与签名的参数为:appId、timeStamp、nonceStr、package、signType,参数区分大小写
下面是我的二次签名代码

Map<String, String> paraMap = new HashMap<String, String>();
paraMap.put("appId",wappId);
paraMap.put("timeStamp", timeStamp);
paraMap.put("nonceStr", nonceStr);
paraMap.put("package",spackage);
paraMap.put("signType",signType);
String SinSring = StringUtil.formatUrlMap(paraMap,false,false);//按照参数名ASCII码从小到大排序(字典序)

String paySign= WXPayUtil.MD5((SinSring+"&key="+key));
paraMap.put("paySign",paySign);

Map<String, Object> mapky = new HashMap<>();
mapky.put("appId", wappId);
mapky.put("timeStamp", timeStamp);
mapky.put("nonceStr", nonceStr);
mapky.put("package", spackage);
mapky.put("signType", signType);
mapky.put("paySign",paySign);

将二次签名的信息发送到客户端
这个是官方给的唤醒 支付界面
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
注:JS API的返回结果get_brand_wcpay_request:ok仅在用户成功完成支付时返回。由于前端交互复杂,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以统一处理为用户遇到错误或者主动放弃,不必细化区分。
示例代码如下:


function onBridgeReady(){
   WeixinJSBridge.invoke(
      'getBrandWCPayRequest', {
         "appId":"wx2421b1c4370ec43b",     //公众号名称,由商户传入     
         "timeStamp":"1395712654",         //时间戳,自1970年以来的秒数     
         "nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串     
         "package":"prepay_id=u802345jgfjsdfgsdg888",     
         "signType":"MD5",         //微信签名方式:     
         "paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 
      },
      function(res){
      if(res.err_msg == "get_brand_wcpay_request:ok" ){
      // 使用以上方式判断前端返回,微信团队郑重提示:
            //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
      } 
   }); 
}
if (typeof WeixinJSBridge == "undefined"){
   if( document.addEventListener ){
       document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
   }else if (document.attachEvent){
       document.attachEvent('WeixinJSBridgeReady', onBridgeReady); 
       document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
   }
}else{
   onBridgeReady();
}

最后支付完成你会收到一个 微信支付的 支付结果通知
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8

就是上面 统一下单 那个地址会得到一个通知成功的信息,只要返回 下面格式的代码就可以了

<xml>

<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>

以上是腾讯的微信支付过程

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,561评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,218评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,162评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,470评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,550评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,806评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,951评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,712评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,166评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,510评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,643评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,306评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,930评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,745评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,983评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,351评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,509评论 2 348

推荐阅读更多精彩内容