springboot[maven] & 微信小程序[1]支付-part1

1.借鉴

微信支付开发者文档,还有很多其他的博客,十分感谢。

2. 开始

【
    具体的流程:
    1.小程序调用我们自己的创建订单接口,然后拼接参数发给微信,微信会返回给我们成功或失败,如果成功,按照微信开发文档拼接map参数返回给小程序[①]
    2:请求1中的接口返回给小程序一个map集合,[②]
    3.小程序将map发送到微信,[③]
    4.等待和处理微信回调[④]
】                
以上序号在下面有用到[①,②,③,④],谢谢搜狗输入法的支持.......

1. 搭建一个springboot项目

2. 几个依赖包

<dependency>
    <groupId>com.github.wxpay</groupId>
    <artifactId>wxpay-sdk</artifactId>
    <version>0.0.3</version>
</dependency>
<dependency>
    <groupId>commons-httpclient</groupId>
    <artifactId>commons-httpclient</artifactId>
    <version>3.1</version>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId> 
    <artifactId>httpclient</artifactId>
    <version>4.5.3</version>
</dependency>

3. 后端准备

3.1 配置信息


# =============================================================================
# =========================        微信小程序支付        ========================
# =============================================================================
# 小程序[mp -> mini program]
# 小程序ID
wx.pay.mp.appid=
# 商户号
wx.pay.mp.mch_id=
# 加密ID - 商户
wx.pay.mp.partnerkey=
# 加密ID - 小程序
wx.pay.mp.partnerkeyMp=
# 终端IP
wx.pay.mp.spbill_create_ip=127.0.0.1
# 用户支付时的回调URL【 TODO 这里需要变更】
wx.pay.mp.notify_url=http://ruihong.frpgz1.idcfengye.com/notify/wxNotify
# 统一下单URL
wx.pay.mp.wechatUnifiedOrderURL=https://api.mch.weixin.qq.com/pay/unifiedorder
# 查询订单URL
wx.pay.mp.wechatOrderQueryURL=https://api.mch.weixin.qq.com/pay/orderquery
# 企业付款URL【用户提现】
wx.pay.mp.wechatTransfersUrl=https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers
# 申请退款【取消订单】
wx.pay.mp.wechatRefundUrl=https://api.mch.weixin.qq.com/secapi/pay/refund
# =============================================================================

3.2 springboot 配置类

@Component
@ConfigurationProperties(value = "wx.pay.mp")
@Getter @Setter
public class WeixinMiniProgramPayProperties
{        
    private String appid;       
    private String mchId;        
    private String spbillCreateIp;        
    private String notifyUrl;        
    private String refundUrl;        
    private String partnerkey;        
    private String partnerkeyMp;        
    private String wechatUnifiedOrderURL;        
    private String wechatOrderQueryURL;        
    private String wechatTransfersUrl;        
    private String wechatRefundUrl;
}

3.3 创建订单

[①,②][service] 返回的map就是要返给小程序的参数,控制器随意

public interface WeixinService
{
     /*** * 创建小程序订单 */
     Map createMiniProgramOrder(String logKey, /** 日志key */
                                String orderNumber, /** 订单号 */
                                String openId, /** 微信用户的openId */
                                String fee, /** 费用,单位分*/
                                String body /** 商品描述 */) throws Exception;


    /*** * 企业向个人付款【提现】 */
    Map enterprice2User(String logKey, String orderNumber, String openId, String fee, String body) throws Exception;


    /** * 申请退款 */
    Map refund(String logKey,
               String refundOrderNumber, /** 申请退款时自己创建的订单号 */
               String transactionId, /** 微信返回的交易ID */
               String openId, /** 微信用户openId */
               String fee /** 费用,单位分*/) throws Exception;

}

[impl][这里面有个httpclient,放后面吧part2中]

import com.github.wxpay.sdk.WXPayUtil;
import WeixinMiniProgramPayProperties;
import WeixinService;
import HttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.*;

@Service("weixinService")
public class WeixinServiceImpl implements WeixinService
{
    private static final Logger logger = LoggerFactory.getLogger(WeixinServiceImpl.class);

    @Autowired WeixinMiniProgramPayProperties weixinPayProperties;

    @Override
    public Map createMiniProgramOrder(String logKey,
                                            String orderNumber,
                                            String openId,
                                            String fee,
                                            String body) throws Exception
    {
        //1.参数封装
        Map param = new HashMap();
        param.put("appid", weixinPayProperties.getAppid());// 公众账号ID
        param.put("mch_id", weixinPayProperties.getMchId());// 商户
        param.put("nonce_str", WXPayUtil.generateNonceStr());// 随机字符串
        param.put("body", body); // 商品描述
        param.put("out_trade_no", orderNumber);// 交易订单号
        param.put("total_fee", fee); // 金额(分)
        param.put("spbill_create_ip", weixinPayProperties.getSpbillCreateIp()); // 终端IP
        param.put("notify_url", weixinPayProperties.getNotifyUrl()); // 通知地址
        param.put("openid", openId); // 用户openid
        param.put("trade_type", "JSAPI"); // 交易类型
        param.put("attach", body); // 附件说明

        logger.info(logKey + " weixinPayProperties" + weixinPayProperties);

        String sign = WXPayUtil.generateSignature(param, weixinPayProperties.getPartnerkey());
        param.put("sign", sign);

        logger.info(logKey + " weixinService#createMiniProgramOrder:" + param);

        HttpClient httpClient = new HttpClient(weixinPayProperties.getWechatUnifiedOrderURL());
        httpClient.setHttps(true);
        httpClient.setXmlParam(xml2String(param));
        httpClient.post(); String xmlResult = httpClient.getContent();

        Map<String, String> mapResult = WXPayUtil.xmlToMap(xmlResult);

        logger.info(logKey + " weixinService#createMiniProgramOrder.wxpay.mp.result:" + mapResult);

        return reSigned(logKey, mapResult.get("prepay_id"));
    }

    private String xml2String(Map 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 = entry.getValue()+"";
            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k))
            {
                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
            } else {
                sb.append("<" + k + ">" + v + "</" + k + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    private Map reSigned(String logKey, String prepay_id) throws Exception
    {
        Map map = new HashMap();
        map.put("appId", weixinPayProperties.getAppid());
        map.put("timeStamp", (System.currentTimeMillis()+"").substring(0, 10));
        map.put("nonceStr", WXPayUtil.generateNonceStr());
        map.put("package", "prepay_id=" + prepay_id);
        map.put("signType", "MD5");
        String sign = addSign(logKey, map);
        map.put("paySign", sign);
        logger.info(logKey + " weixinService#reSigned:" + map);
        return map;
    }

    private String addSign(String logKey, Map hashMap2Sign) throws Exception
    {
        StringBuffer sign = new StringBuffer();
        List<Map.Entry<Object, Object>> infoIds = new ArrayList<Map.Entry<Object, Object>>(hashMap2Sign.entrySet());
        Collections.sort(infoIds, new Comparator<Map.Entry<Object, Object>>()
        {
            public int compare(Map.Entry<Object, Object> o1, Map.Entry<Object, Object> o2)
            {
                 return (o1.getKey()).toString().compareTo((String) o2.getKey());
            }
        });

        for(int i = 0;i < infoIds.size();i ++)
        {
            String key = (String) infoIds.get(i).getKey();
            if(sign.length() == 0)
            {    
                sign.append(key).append("=")
                    .append(hashMap2Sign.get(key).toString().toLowerCase());
            }
            else
            {
                sign.append("&").append(key)
                    .append("=").append(hashMap2Sign.get(key));
            }
        }
        sign.append("&key="+weixinPayProperties.getPartnerkey());
        logger.info(logKey + " addSign:" + sign.toString()); return     
        WXPayUtil.MD5(sign.toString()).toUpperCase();
    }
}

3.4 微信回调类

import com.github.wxpay.sdk.WXPayUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;import java.util.SortedMap;
import java.util.TreeMap;

@RestController
@RequestMapping(value = "/notify")
public class NotifyController

{
    @RequestMapping("/wxNotify")
    public String wxNotify(HttpServletRequest request,
                            HttpServletResponse response) throws Exception

    {
        request.setCharacterEncoding("UTF-8");                 
        response.setCharacterEncoding("UTF-8");                 r
        esponse.setContentType("text/html;charset=UTF-8");                 
        response.setHeader("Access-Control-Allow-Origin", "*");
        StringBuilder sb = new StringBuilder();
        String line = "";
        try
        {
            while ((line = request.getReader().readLine()) != null)
            {
                    sb.append(line); 
            }
            request.getReader().close();
        } catch (Exception e)
        {
            e.printStackTrace();
            return xml("fail","xml获取失败");
        }
        String result = sb.toString();
        if (StringUtils.isEmpty(result))
        {
            return xml("fail","xml为空");
        }
        Map<String, String> stringStringMap = WXPayUtil.xmlToMap(result);
        String total_fee = stringStringMap.get("total_fee");// 获取订单金额
        String trade_type = stringStringMap.get("trade_type");//交易类型
        String return_code = stringStringMap.get("return_code");// SUCCESS/FAIL    
        String attach = stringStringMap.get("attach");// 附件说明(消费,充值)
        String transaction_id = stringStringMap.get("transaction_id");//微信支付订单号
        String out_trade_no = stringStringMap.get("out_trade_no");// 获取商户订单号

        if (!"SUCCESS".equalsIgnoreCase(return_code))
        {
           return xml("fail", "支付失败!");
        }
        // 尤其要注意的是要保证幂等性
        // 从订单表中查询出数据
        // 更新订单状态等等操作
        // 操作用户积分,余额
        return xml("SUCCESS", "OK");
    }

    //通过xml 发给微信消息

    public String xml(String return_code, String return_msg)
    {
        SortedMap<String, String> parameters = new TreeMap<String, String>();
        parameters.put("return_code", return_code);
        parameters.put("return_msg", return_msg); return "<xml><return_code><![CDATA[" + return_code + "]]>" + "</return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
    }

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

推荐阅读更多精彩内容