微信支付-公众号支付-JSAPI调用(V2——Java)

1.开发微信公众号支付准备资料

①APPID,这个数据我们可以从“申请微信支付成功”的邮件中获取。

②AppSecret,这个数据,大家可以看上图中获取。

③Mch_id,这是值是微信支付商户号,大家可以从邮件中获取

④KEY,这个参数KEY是在商户后台配置的一个32位的key,微信商户平台-账户设置-安全设置-api安全,在这里设置。这个值是可以自行设置的。


微信支付必须参数

2.设置支付目录

支付授权目录说明:

1、商户最后请求拉起微信支付收银台的页面地址我们称之为“$\color{red}{支付目录}$”,例如:https://www.weixin.com/pay.php。

2、商户实际的支付目录必须和在微信支付商户平台设置的一致,否则会报错“当前页面的URL未注册:”

支付授权目录设置说明:

登录微信支付商户平台(pay.weixin.qq.com)-->产品中心-->开发配置,设置后一般5分钟内生效。

支付授权目录校验规则说明:

1、如果支付授权目录设置为顶级域名(例如:https://www.weixin.com/ ),那么只校验顶级域名,不校验后缀;

2、如果支付授权目录设置为多级目录,就会进行全匹配,例如设置支付授权目录为https://www.weixin.com/abc/123/,则实际请求页面目录不能为https://www.weixin.com/abc/,也不能为https://www.weixin.com/abc/123/pay/,必须为https://www.weixin.com/abc/123/


设置支付目录

具体看官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3

3.首先要看微信支付的业务流程,官方文档——>开发微信支付


业务流程时序图

微信支付的流程(主要步骤):

1、用户访问微信OAuth2.0网站,通过OAuth2.0的重定向获得code

2、根据code获得用户的标识符openid,这个参数在调用统一下单接口中会用到

3、调用统一下单API获得prepay_id

4、获得prepay_id后,接下来要调用通过“网页端调起支付API”,这个调用的API需要一系列参数,这些参数我们在后台进行组装,通过JSON传到前台去

5、获得数据后通过调用JSPAI发起微信支付

6、用户输入支付密码,支付完成

7、等待微信回调,回调中处理业务

8、支付流程完毕

微信支付所需依赖:

        <dependency>

            <groupId>commons-codec</groupId>

            <artifactId>commons-codec</artifactId>

            <version>1.6</version>

        </dependency>

        <dependency>

            <groupId>commons-logging</groupId>

            <artifactId>commons-logging</artifactId>

            <version>1.1.3</version>

        </dependency>

        <dependency>

            <groupId>org.apache.httpcomponents</groupId>

            <artifactId>fluent-hc</artifactId>

            <version>4.3.6</version>

        </dependency>

        <dependency>

            <groupId>org.apache.httpcomponents</groupId>

            <artifactId>httpclient</artifactId>

            <version>4.3.6</version>

        </dependency>

        <dependency>

            <groupId>org.apache.httpcomponents</groupId>

            <artifactId>httpclient-cache</artifactId>

            <version>4.3.6</version>

        </dependency>

        <dependency>

            <groupId>org.apache.httpcomponents</groupId>

            <artifactId>httpcore</artifactId>

            <version>4.3.3</version>

        </dependency>

        <dependency>

            <groupId>org.apache.httpcomponents</groupId>

            <artifactId>httpmime</artifactId>

            <version>4.3.6</version>

        </dependency>

        <dependency>

            <groupId>org.jsoup</groupId>

            <artifactId>jsoup</artifactId>

            <version>1.7.2</version>

        </dependency>

        <!-- FastJson -->

        <dependency>

            <groupId>com.alibaba</groupId>

            <artifactId>fastjson</artifactId>

            <version>1.2.1</version>

        </dependency>

    </dependencies>


4、微信支付整体流程实现

1.获取code值,我这边是通过$\color{red}{前端写的js}$来获取code值的


通过该JS来获取code值

2.通过Code 获取用户 openid 和 access_token

import com.alibaba.fastjson.JSON;

import com.alibaba.fastjson.JSONObject;

import ***********************.WeChatConst;//一些常量

import org.apache.http.HttpResponse;

import org.apache.http.client.methods.HttpPost;

import org.apache.http.entity.StringEntity;

import org.apache.http.impl.client.DefaultHttpClient;

import org.apache.http.util.EntityUtils;

import java.io.*;

import java.nio.charset.Charset;

import java.util.HashMap;

import java.util.Map;

import java.util.Random;

/**

* Created by Arthur on 2015/11/10 16:45.

*/

public class WeChatUtil {

    /**

    * 通过Code 获取用户 openid 和 access_token

    * @param code

    * @return

    * @throws IOException

    */

    public static Map<String,Object> getOpenIdByCode(String code) throws IOException {

        /**

        * 设置访问路径

        */

        HttpPost httppost = new HttpPost("https://api.weixin.qq.com/sns/oauth2/access_token");

        /**

        * 组装请求参数

        */

        String reqEntityStr = "appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";

        reqEntityStr = reqEntityStr.replace("APPID", WeChatConst.APPID);

        reqEntityStr = reqEntityStr.replace("SECRET", WeChatConst.APP_SECRET);

        reqEntityStr = reqEntityStr.replace("CODE", code);

        StringEntity reqEntity = new StringEntity(reqEntityStr);

        /**

        * 设置浏览器

        */

        DefaultHttpClient httpclient = new DefaultHttpClient();

        /**

        * 设置参数

        */

        httppost.setEntity(reqEntity);

        /**

        * 发起请求

        */

        HttpResponse response = httpclient.execute(httppost);

        /**

        * 获得请求内容

        */

        String strResult = EntityUtils.toString(response.getEntity(), Charset.forName("utf-8"));

        /**

        * 分析内容

        */

        JSONObject jsonObject = (JSONObject) JSON.parse(strResult);

        Map<String,Object> map = new HashMap<>();

        map.put("openid",jsonObject.get("openid"));

        map.put("access_token",jsonObject.get("access_token"));

        map.put("refresh_token",jsonObject.get("refresh_token"));

        return map;

    }

    /**

    * 统一下单

    * 获得PrePayId

    * @param body  商品或支付单简要描述

    * @param out_trade_no 商户系统内部的订单号,32个字符内、可包含字母

    * @param total_fee  订单总金额,单位为分

    * @param IP    APP和网页支付提交用户端ip

    * @param notify_url 接收微信支付异步通知回调地址

    * @param openid 用户openId

    * @throws IOException

    */

    public static String unifiedorder(String body,String out_trade_no,Integer total_fee,String IP,String notify_url,String openid)throws IOException {

        /**

        * 设置访问路径

        */

        HttpPost httppost = new HttpPost("https://api.mch.weixin.qq.com/pay/unifiedorder");

        /**

        * 组装请求参数

        * 按照ASCII排序

        */

        String reqEntityStr = "appid=APPID" +

                "&body=BODY" +

                "&mch_id=MCH_ID" +

                "&nonce_str=NONCE_STR" +

                "&notify_url=NOTIFY_URL" +

                "&openid=OPENID" +

                "&out_trade_no=OUT_TRADE_NO" +

                "&spbill_create_ip=IP" +

                "&total_fee=TOTAL_FEE" +

                "&trade_type=TRADE_TYPE"

                ;//这个字段是用于之后MD5加密的,字段要按照ascii码顺序排序

        /**

        * 设置数据

        */

        String nonce_str = getNonceStr().toUpperCase();//随机数据

        reqEntityStr = reqEntityStr.replace("APPID", WeChatConst.APPID);

        reqEntityStr = reqEntityStr.replace("MCH_ID", WeChatConst.MCH_ID);

        reqEntityStr = reqEntityStr.replace("NONCE_STR", nonce_str);

        reqEntityStr = reqEntityStr.replace("BODY", body);

        reqEntityStr = reqEntityStr.replace("OUT_TRADE_NO", out_trade_no);

        reqEntityStr = reqEntityStr.replace("TOTAL_FEE", total_fee.toString());

        reqEntityStr = reqEntityStr.replace("IP", IP);

        reqEntityStr = reqEntityStr.replace("NOTIFY_URL", notify_url);

        reqEntityStr = reqEntityStr.replace("TRADE_TYPE", WeChatConst.TRADE_TYPE);

        reqEntityStr = reqEntityStr.replace("OPENID", openid);

        /**

        * 生成sign的数据

        */

        String sign = reqEntityStr + "&key="+WeChatConst.KEY;

        sign = MD5Util.MD5(sign).toUpperCase();

        /**

        * 组装XML

        */

        StringBuilder sb = new StringBuilder("");

        sb.append("<xml>");

        setXmlKV(sb,"appid",WeChatConst.APPID);

        setXmlKV(sb,"body",body);

        setXmlKV(sb,"mch_id",WeChatConst.MCH_ID);

        setXmlKV(sb,"nonce_str",nonce_str);

        setXmlKV(sb,"notify_url",notify_url);

        setXmlKV(sb,"openid",openid);

        setXmlKV(sb,"out_trade_no",out_trade_no);

        setXmlKV(sb,"spbill_create_ip",IP);

        setXmlKV(sb,"total_fee",total_fee.toString());

        setXmlKV(sb,"trade_type",WeChatConst.TRADE_TYPE);

        setXmlKV(sb,"sign",sign);

        sb.append("</xml>");

//        reqEntityStr = reqEntityStr.replace("sign", sign);

        StringEntity reqEntity = new StringEntity(new String (sb.toString().getBytes("UTF-8"),"ISO8859-1"));//这个处理是为了防止传中文的时候出现签名错误

        httppost.setEntity(reqEntity);

        DefaultHttpClient httpclient = new DefaultHttpClient();

        HttpResponse response = httpclient.execute(httppost);

        String strResult = EntityUtils.toString(response.getEntity(), Charset.forName("utf-8"));

//        System.out.println(strResult);

        return getPrePayId(strResult);

    }

    /**

    * 获得32位随机字符串

    * @return

    */

    public static String getNonceStr(){

        String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

        StringBuilder sb = new StringBuilder();

        Random rd = new Random();

        for(int i = 0 ; i < 32 ; i ++ ){

            sb.append(str.charAt(rd.nextInt(str.length())));

        }

        return sb.toString();

    }

    /**

    * 插入XML标签

    * @param sb

    * @param Key

    * @param value

    * @return

    */

    public static StringBuilder setXmlKV(StringBuilder sb,String Key,String value){

        sb.append("<");

        sb.append(Key);

        sb.append(">");

        sb.append(value);

        sb.append("</");

        sb.append(Key);

        sb.append(">");

        return sb;

    }

    /**

    * 解析XML  获得 PrePayId

    * @param xml

    * @return

    */

    public static String getPrePayId(String xml){

        int start = xml.indexOf("<prepay_id>");

        int end = xml.indexOf("</prepay_id>");

        if(start < 0 && end < 0){

            return null;

        }

        return xml.substring(start + "<prepay_id>".length(),end)

                .replace("<![CDATA[","").replace("]]>","");

    }

}

public interface WeChatConst {

    String APPID = "";//填写APPID

    String APP_SECRET = "";//填写AppSecret

    String MCH_ID = "";//Mch_id,这是值是微信支付商户号,大家可以从邮件中获取

    String KEY = "";//这个参数KEY是在商户后台配置的一个32位的key,微信商户平台-账户设置-安全设置-api安全

    /**

    * 交易类型

    */

    String TRADE_TYPE = "JSAPI";

}

我们可以调用里面的“getOpenIdByCode”方法通过code 获得openid,这个openid我们先记录下来,稍后会用到的,接下来我们要经行调用统一下单接口

3.然后通过可以调用里面的“unifiedorder”获取prepay_id

后台调佣统一下单的API,获得prepay_id

**这步我们先要设置支付授权目录**

微信公众平台→微信支付→开发配置→测试授权目录和测试白名单。

直接配置支付授权目录也行。

后台获得到需要用户支付的金额,以及一些列的产品信息后,系统经行预下单,也是就调用调用统一下单的API,生成一个需要支付的订单,获得prepay_id,该值用于在前端页面,通过JSAPI中调用微信支付需要用到,代码看下面的  "JSONObjectdoRecharge(HttpServletRequest request, String code)" 的方法,以及WeChatUtil。

统一下单大家可以调用我刚刚提供给大家的工具类中的WeChatUtil.unifiedorder这个方法,参数在方法上面的注释有写清楚。

这里如果你们想自行封装需要注意一个编码问题,这个会导致你回调产生签名错误。

获得prepay_id后,我们需要在后台组装,调用微信JSAPI的接口数据。

以下是需要组装起来的参数,也是进行微信支付的必须参数

"appId": "", //公众号ID,由商户传入

"timeStamp": "", //时间戳 

"nonceStr": "", //随机串   

"package": "", //获得PrePayId

"signType": "MD5", //微信签名方式:   

"paySign": "" //微信签名

4.前端所需参数,通过这些参数可以进行支付

获取这些参数来源是需要——>进行预下单——>方法如下:

/**

* 微信支付——进行预下单

*

* @param request

* @param code

* @return

* @throws IOException

*/

@RequestMapping(value ="/WxPay")

public static JSONObjectdoRecharge(HttpServletRequest request, String code)throws IOException {

//获取openId的方法

    Map openIdByCode = WeChatUtil.getOpenIdByCode(code);

    System.out.println("openId:" + openIdByCode.get("openid"));

    String body = WeChatConst.BOBY;

    //商户订单号

    String out_trade_no = UUIDUtils.getUUID().toUpperCase();

    System.out.println("商户订单号:" + out_trade_no);

    //支付金额

    int total_fee =1;

    //IP

    String IP = WeChatConst.IP;

    //支付成功后回调的地址

    String openid = (String) openIdByCode.get("openid");

    //在业务层调用 WeChatUtil.unifiedorder方法获得prepay_id

    String prepay = WeChatUtil.unifiedorder(body, out_trade_no, total_fee, IP, WeChatConst.NOTIFY_URL, openid);

    System.out.println("prepay_id" + prepay);

    //将参数组装起来

    String prepay_id = prepay;

    //1970年到现在的秒数

    String timeStamp = String.valueOf((System.currentTimeMillis() /1000));

    //数据字符串

    String nonceStr = WeChatUtil.getNonceStr().toUpperCase();

    //prepay_id

    String packageStr ="prepay_id=" + prepay_id;

    String signType ="MD5";

    //签名

    String paySign =

"appId=" + WeChatConst.APPID +

"&nonceStr=" + nonceStr +

"&package=prepay_id=" + prepay_id +

"&signType=" + signType +

"&timeStamp=" + timeStamp +

"&key=" + WeChatConst.KEY;//注意这里的参数要根据ASCII码 排序

    //将数据MD5加密

    paySign = Md5Utils.encrypt(paySign).toUpperCase();

    JSONObject object =new JSONObject();

    object.put("appId", WeChatConst.APPID);

    object.put("timeStamp", timeStamp);

    object.put("nonceStr", nonceStr);

    object.put("package", packageStr);

    object.put("signType", signType);

    object.put("paySign", paySign);

    return object;

}

通过该方法——>JSONObjectdoRecharge——>获取进行支付参数

5.将这些参数(appId、nonceStr、package、signType、timeStamp、key)填写到如下页面:

支付页面wxPay.html

5、将该参数设置好的页面HTML放到 "支付授权目录"(授权目录第3步骤有讲到)

6、直接访问该html文件,就可以进行支付了

比如我存放到服务器当中的https://xx.lxx-xx.com/h5/fjyc/wxPay.html 目录

而这个项目当中目录是支付授权目录,那我直接访问该链接(https://xx.lxx-xx.com/h5/fjyc/wxPay.html )


唤醒支付


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

推荐阅读更多精彩内容