初入微信支付

1 开发准备

开发文档

微信支付接口调用的整体思路:

按API要求组装参数,以XML方式发送(POST)给微信支付接口(URL),微信支付接口也是以XML方式给予响应。程序根据返回的结果(其中包括支付URL)生成二维码或判断订单状态。

在线微信支付开发文档:

https://pay.weixin.qq.com/wiki/doc/api/index.html

微信支付模式介绍

模式一

  1. 商户后台系统根据微信支付规定格式生成二维码(规则见下文),展示给用户扫码。
  2. 用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
  3. 微信支付系统收到客户端请求,发起对商户后台系统支付回调URL的调用。调用请求将带productid和用户的openid等参数,并要求商户系统返回交数据包
  4. 商户后台系统收到微信支付系统的回调请求,根据productid生成商户系统的订单。
  5. 商户系统调用微信支付【统一下单API】请求下单,获取交易会话标识(prepay_id)
  6. 微信支付系统根据商户系统的请求生成预支付交易,并返回交易会话标识(prepay_id)。
  7. 商户后台系统得到交易会话标识prepay_id(2小时内有效)。
  8. 商户后台系统将prepay_id返回给微信支付系统。返回数
  9. 微信支付系统根据交易会话标识,发起用户端授权支付流程
  10. 用户在微信客户端输入密码,确认支付后,微信客户端提交支付授权。
  11. 微信支付系统验证后扣款,完成支付交易。
  12. 微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
  13. 微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
  14. 未收到支付通知的情况,商户后台系统调用【查询订单API】。
  15. 商户确认订单已支付后给用户发货。

微信支付SDK

两个依赖

HttpClient工具类

HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。HttpClient已经应用在很多的项目中,比如Apache Jakarta上很著名的另外两个开源项目Cactus和HTMLUnit都使用了HttpClient。

HttpClient通俗的讲就是模拟了浏览器的行为,如果我们需要在后端向某一地址提交数据获取结果,就可以使用HttpClient.

关于HttpClient(原生)具体的使用不属于我们本章的学习内容,我们这里这里为了简化HttpClient的使用,提供了工具类HttpClient(对原生HttpClient进行了封装)

package com.zb.util;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class HttpClient {
    private String url;
    private Map<String, String> param;
    private int statusCode;
    private String content;
    private String xmlParam;
    private boolean isHttps;

    public boolean isHttps() {
        return isHttps;
    }

    public void setHttps(boolean isHttps) {
        this.isHttps = isHttps;
    }

    public String getXmlParam() {
        return xmlParam;
    }

    public void setXmlParam(String xmlParam) {
        this.xmlParam = xmlParam;
    }

    public HttpClient(String url, Map<String, String> param) {
        this.url = url;
        this.param = param;
    }

    public HttpClient(String url) {
        this.url = url;
    }

    public void setParameter(Map<String, String> map) {
        param = map;
    }

    public void addParameter(String key, String value) {
        if (param == null)
            param = new HashMap<String, String>();
        param.put(key, value);
    }

    public void post() throws ClientProtocolException, IOException {
        HttpPost http = new HttpPost(url);
        setEntity(http);
        execute(http);
    }

    public void put() throws ClientProtocolException, IOException {
        HttpPut http = new HttpPut(url);
        setEntity(http);
        execute(http);
    }

    public void get() throws ClientProtocolException, IOException {
        if (param != null) {
            StringBuilder url = new StringBuilder(this.url);
            boolean isFirst = true;
            for (String key : param.keySet()) {
                if (isFirst) {
                    url.append("?");
                }else {
                    url.append("&");
                }
                url.append(key).append("=").append(param.get(key));
            }
            this.url = url.toString();
        }
        HttpGet http = new HttpGet(url);
        execute(http);
    }

    /**
     * set http post,put param
     */
    private void setEntity(HttpEntityEnclosingRequestBase http) {
        if (param != null) {
            List<NameValuePair> nvps = new LinkedList<NameValuePair>();
            for (String key : param.keySet()) {
                nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数
            }
            http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数
        }
        if (xmlParam != null) {
            http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));
        }
    }

    private void execute(HttpUriRequest http) throws ClientProtocolException,
            IOException {
        CloseableHttpClient httpClient = null;
        try {
            if (isHttps) {
                SSLContext sslContext = new SSLContextBuilder()
                        .loadTrustMaterial(null, new TrustStrategy() {
                            // 信任所有
                            @Override
                            public boolean isTrusted(X509Certificate[] chain,
                                                     String authType)
                                    throws CertificateException {
                                return true;
                            }
                        }).build();
                SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                        sslContext);
                httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
                        .build();
            } else {
                httpClient = HttpClients.createDefault();
            }
            CloseableHttpResponse response = httpClient.execute(http);
            try {
                if (response != null) {
                    if (response.getStatusLine() != null) {
                        statusCode = response.getStatusLine().getStatusCode();
                    }
                    HttpEntity entity = response.getEntity();
                    // 响应内容
                    content = EntityUtils.toString(entity, Consts.UTF_8);
                }
            } finally {
                response.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            httpClient.close();
        }
    }

    public int getStatusCode() {
        return statusCode;
    }

    public String getContent() throws ParseException, IOException {
        return content;
    }
}

支付微服务搭建

创建application.yml,配置文件如下:

server:
  port: 9060
spring:
  application:
    name: pay-server
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
weixin:
  appid: wxab8acb865bb1637e   #公众账号ID
  partner: 11473623  #商户号
  partnerkey: 2ab9071b06b9f739b950ddb41db2690d  #商户密钥
  notifyurl: http://zpxk4c.natappfree.cc/pay/notifyurl #回调地址

回调地址每次启动natapp.exe 随机生成

 NATAPP-内网穿透 基于ngrok的国内高速内网映射工具

进行注册

代码实现

业务层

package com.zb.service.impl;

import com.github.wxpay.sdk.WXPayUtil;
import com.zb.service.PayService;
import com.zb.util.HttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class PayServiceImpl implements PayService {

    @Value("${weixin.appid}")
    private String appid;
    @Value("${weixin.partner}")
    private String partner;
    @Value("${weixin.partnerkey}")
    private String partnerkey;
    @Value("${weixin.notifyurl}")
    private String notifyurl;

    @Override
    public Map<String, String> createCodeUrl(String trandNo, Integer money) {
        try {
            Map<String, String> param = new HashMap<>();
            param.put("appid", appid);
            param.put("mch_id", partner);
            param.put("nonce_str", WXPayUtil.generateNonceStr());
            param.put("body", "我的我的");
            param.put("out_trade_no", trandNo);
            param.put("total_fee", money.toString());
            param.put("spbill_create_ip", "127.0.0.1");
            param.put("notify_url", notifyurl);
            param.put("trade_type", "NATIVE");
            String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
            String xml = WXPayUtil.generateSignedXml(param, partnerkey);
            //创建网络链接
            HttpClient httpClient = new HttpClient(url);
            httpClient.setXmlParam(xml);
            httpClient.setHttps(true);
            httpClient.post();
            String content = httpClient.getContent();
            return WXPayUtil.xmlToMap(content);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

控制层

package com.zb.controller;

import com.zb.service.PayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@RestController
@RequestMapping("/pay")
public class PayController {

    @Autowired
    private PayService payService;

    @GetMapping("/create")
    public Map<String, String> createCodeUrl(@RequestParam("trandNo") String trandNo, @RequestParam("money") Integer money) {
        System.out.println(trandNo + "\t" + money);
        return payService.createCodeUrl(trandNo, money);
    }


    @GetMapping("/say")
    public String say(String name) {
        return "hello:" + name;
    }


    @RequestMapping("notifyurl")
    public String notifyurl(HttpServletRequest request) {
        System.out.println("回调方法...");
        return "success";
    }

}

这里我们订单号通过随机数生成,金额暂时写死,后续开发我们再对接业务系统得到订单号和金额

Postman测试

通过 code_url中的路径

打开支付页面/pay.html,修改value路径,然后打开,会出现二维码,可以扫码

支付信息回调

接口分析

每次实现支付之后,微信支付都会将用户支付结果返回到指定路径,而指定路径是指创建二维码的时候填写的notifyurl参数,响应的数据以及相关文档参考一下地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8

回调接收数据实现

修改shop.parent微服务的com.zb.controller.PayController,添加回调方法代码如下

@RequestMapping(value = "/notify/url")
public String notifyUrl(HttpServletRequest request){
    InputStream inStream;
    try {
        //读取支付回调数据
        inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        outSteam.close();
        inStream.close();
        // 将支付回调数据转换成xml字符串
        String result = new String(outSteam.toByteArray(), "utf-8");
        //将xml字符串转换成Map结构
        Map<String, String> map = WXPayUtil.xmlToMap(result);
        System.out.println("获取接收到的数据;"+map);
        //响应数据设置
        Map respMap = new HashMap();
        respMap.put("return_code","SUCCESS");
        respMap.put("return_msg","OK");
        return WXPayUtil.mapToXml(respMap);
    } catch (Exception e) {
        e.printStackTrace();
        //记录错误日志
    }
    return null;
}

直接扫描支付,的回调方法自动执行 查看运行效果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值