微信公众号支付(一)统一下单

最近在研究微信公众号的支付开发,一开始对着开发文档各种懵,也自然而然地跳入了各种坑,现在把整个开发过程简略地做个记录。

1.开发环境准备

首先要有一个微信服务号,订阅号是不能开通微信支付的。微信公众号申请微信支付后,接着申请微信支付商户平台,公众号上面已经标明“公众平台微信支付公众号支付授权目录、扫码支付回调URL配置入口已于8月1日迁移至商户平台(pay.weixin.qq.com)。迁移后,原有配置数据不会受影响,你可在商户平台查看和配置。带来的不便敬请谅解”。所以后续开发所需要的配置项基本都在商户平台中进行。


2.开发开始

首先还是得先看下微信官方提供的公众号支付开发文档,大概对整个开发的流程有个理解。主要是看API列表

开发文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1


首先看到的就是统一下单,没错,支付开发的第一步就是实现统一下单。
2.1 构造统一下单对象
首先,构造一个统一下单对象,对象的字段包括里面的必填字段,有额外需要的可以自己添加。特别注意里面对请求参数的描述。
/**
 * 统一下单
 */
@XmlRootElement(name = "xml")
public class UnifiedOrder {

    /**
     * 公众账号ID
     */
    private String appid;
    /**
     * 商户号
     */
    private String mch_id;
    /**
     * 附加数据(说明)
     */
    private String attach;
    /**
     * 商品描述
     */
    private String body;
    /**
     * 随机串
     */
    private String nonce_str;
    /**
     * 通知地址
     */
    private String notify_url;
    /**
     * 用户标识
     */
    private String openid;
    /**
     * 商户订单号
     */
    private String out_trade_no;
    /**
     * 终端IP(用户)
     */
    private String spbill_create_ip;
    /**
     * 总金额
     */
    private Integer total_fee;
    /**
     * 交易类型
     */
    private String trade_type;
    /**
     * 签名
     */
    private String sign;
    /**
     * 签名方式
     */
    private String signType;
    /**
     * WEB
     */
    private String device_info;

    /**
     * 统一下单接口
     */
    private String prepay_id;

    /**
     * 时间戳
     * @return
     */
    private String timeStamp;
后台创建一个接口,后面H5支付的时候要调用,填充这个统一下单对象,appid,mchid等可以封装成参数。微信需要接收的是xml的数据。所以我们还得把封装好的对象转化成xml。官网的提供的demo也有utils方法。demo在这下载。

2.2 获取openid,对于微信公众号支付,统一下单时openid是必须的。因此在统一下单之前还得获取openid。首先要到公众号后台进行授权配置。官网说明:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
通过 公众号可以通过微信网页授权机制,来获取用户基本信息,一般只要scope为snsapi_userinfo,也是静默授权即可。

如果使用的是默认授权,那么就不会出现用户确认的提示。如图所示

获取openid,一般是通过js调起微信授权。所以可在调起支付的页面进行js授权,url配置公众号的appid,后台回调地址,授权成功后,微信会调用这个接口,并给我们返回用户的信息。
window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=公众号的appida&redirect_uri=回调接口(浏览器能访问的接口)&response_type=code&scope=snsapi_base&state=ssssqqqq#wechat_redirect";
接口调用成功后,微信调用后台接口,在这我们要根据微信返回的code来获取用户信息,并将其进行转换,然后放到session中,确保获取的是当前登录用户的信息。以下为上面所写的回调接口。url配置appid,secret为公众号秘钥即AppSecret,可在公众号后台的基本配置中进行查看。code为微信授权返回的code。

/**
 * 获取openid
 *
 * @return
 */
@RequestMapping("auth")
public String auth(String code, String state, HttpServletRequest request) {
    HttpSession session = request.getSession();
    String format = MessageFormat.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code", Constant.APPID, Constant.SECRET, code);
    String s = HttpUtils.get(format);
    Map map = JSON.parseObject(s, Map.class);
    //String userInfo = MessageFormat.format(url_user, Constant.APPID, Constant.SECRET, code);
    SecurityUser currentUser = SecurityUserUtils.getCurrentUser(request);
    if (currentUser != null) {
        session.setAttribute("openid", map.get("openid"));
    }
    return "redirect:http://www.XXX.com/index.html";
}
到这里,openid就已经获取到了。然后统一下单对象setOpenid(session.getAttribute("openid")。构造一个完整的统一下单对象。
//构建统一下单对象
UnifiedOrder unifiedOrder = new UnifiedOrder();
unifiedOrder.setAppid(Constant.APPID);
unifiedOrder.setDevice_info(Constant.DEVICE_INFO);
unifiedOrder.setMch_id(Constant.MCHID);
unifiedOrder.setNonce_str(Constant.NONCE_STR);
unifiedOrder.setBody("充值" + uiorder.getTotalFee() + "元");
unifiedOrder.setAttach("自动充值");
unifiedOrder.setSpbill_create_ip(request.getRemoteAddr());
unifiedOrder.setNotify_url(Constant.NOTIFY_URL);
unifiedOrder.setTrade_type(Constant.TRADE_TYPE);
unifiedOrder.setOpenid((String) request.getSession().getAttribute("openid"));
BigDecimal totalFee = uiorder.getTotalFee();
unifiedOrder.setTotal_fee(totalFee.multiply(new BigDecimal(100)).intValue());
unifiedOrder.setOut_trade_no(SequenceUtils.generateOrderNo(date));
2.3 第一次签名
统一下单之前还得对统一下单对象设置签名,根据官方文档的说明进行签名生成。key在商户平台的api秘钥中进行设置

以下提供签名生成的方法。MD5加密等方法都可以在官网的sdk例子中找得到。

签名生成完成,这时候我们把统一下单对象打包成xml。发送给微信的接口
https://api.mch.weixin.qq.com/pay/unifiedorder
SignUtils.sign(unifiedOrder);
String s = XmlParserUtil.object2Xml(unifiedOrder);
//统一下单
String s1 = HttpUtils.postXML("https://api.mch.weixin.qq.com/pay/unifiedorder", s);
UnifiedOrder order = XmlParserUtil.xml2Object(s1, UnifiedOrder.class);
HttpUtils的postXML方法如下
public static String postXML(String url, String xml) {
    RequestBody body = RequestBody.create(XML, xml);
    Request request = new Request.Builder()
            .post(body)
            .url(url)
            .addHeader("Accept", "application/xml")
            .build();
    Call call = httpClient.newCall(request);
    try {
        Response response1 = call.execute();
        return response1.body().string();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}
到了这里,统一下单就完成了。此时微信给我们返回的order对象就包括一个很重要的字段
prepay_id
这个对象用来做后面的H5支付调起是非常重要的。下一章节我们将介绍如何进行H5调起微信支付。

猜你喜欢

转载自blog.csdn.net/zsm15623544116/article/details/79100072