微信小程序实现支付功能,Java版。


支付主要开发流程:

第一步:准备数据(商户号、商户密钥、appid、小程序密钥、商户证书密钥等)。准备基于httpclient的http请求工具类HttpUtils。准备支付API对应的SDK。
第二步:编写controller,接收前端发送过来的openid、支付金额等信息。
第三步:编写service,将琐碎的参数拼接加密,为调用支付统一下单API做准备,也为获取签名paySign做准备。
第四步:将主备好的参数传给小程序,小程序调用requestPayment进行支付业务


一、准备数据、工具类和SDK

1.先编写WechatConstant

Tip:就是先将开发小程序大概率要用到的信息封装到一个类里。也不知道这个说法对不对,就是为了用着舒

public class WechatConstant {
    public final static String MCH_ID = "****************"; //商户号
    public final static String MCH_KEY = "***************"; //商户密钥
    public final static String APPID = "*****************"; //appid
    public final static String SECRET = "****************"; //小程序密钥
    public final static String KEY = "*******************"; //商户证书密钥
}

2.编写请求工具类HttpUtils

Tip:这个百度有很多,比我的漂亮优雅的。。

public class HttpUtil {

    private static final String Charset = "utf-8";


    /**
     * 发送请求,如果失败,会返回null
     * @param url
     * @param map
     * @return
     */
    public static String post(String url, Map<String, String> map) {
        // 处理请求地址
        try {
            HttpClient client = HttpClientBuilder.create().build();
            URI uri = new URI(url);
            HttpPost post = new HttpPost(uri);

            // 添加参数
            List<NameValuePair> params = new ArrayList<NameValuePair>();
            for (String str : map.keySet()) {
                params.add(new BasicNameValuePair(str, map.get(str)));
            }
            post.setEntity(new UrlEncodedFormEntity(params, Charset));
            // 执行请求
            HttpResponse response = client.execute(post);

            if (response.getStatusLine().getStatusCode() == 200) {
                // 处理请求结果
                StringBuffer buffer = new StringBuffer();
                InputStream in = null;
                try {
                    in = response.getEntity().getContent();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in,Charset));
                    String line = null;
                    while ((line = reader.readLine()) != null) {
                        buffer.append(line);
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    // 关闭流
                    if (in != null)
                        try {
                            in.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                }

                return buffer.toString();
            } else {
                return null;
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return null;

    }

    /**
     * 发送请求,如果失败会返回null
     * @param url
     * @param str
     * @return
     */
    public static String post(String url, String str) {
        // 处理请求地址
        try {
            HttpClient client = HttpClientBuilder.create().build();
            URI uri = new URI(url);
            HttpPost post = new HttpPost(uri);
            post.setEntity(new StringEntity(str, Charset));
            // 执行请求
            HttpResponse response = client.execute(post);

            if (response.getStatusLine().getStatusCode() == 200) {
                // 处理请求结果
                StringBuffer buffer = new StringBuffer();
                InputStream in = null;
                try {
                    in = response.getEntity().getContent();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in,"utf-8"));
                    String line = null;
                    while ((line = reader.readLine()) != null) {
                        buffer.append(line);
                    }

                } finally {
                    // 关闭流
                    if (in != null)
                        in.close();
                }

                return buffer.toString();
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * 发送GET方式的请求,并返回结果字符串。
     * <br>
     * 时间:2017年2月27日,作者:http://wallimn.iteye.com
     * @param url
     * @return 如果失败,返回为null
     */
    public static String get(String url) {
        try {
            HttpClient client = HttpClientBuilder.create().build();
            URI uri = new URI(url);
            HttpGet get = new HttpGet(uri);
            HttpResponse response = client.execute(get);
            if (response.getStatusLine().getStatusCode() == 200) {
                StringBuffer buffer = new StringBuffer();
                InputStream in = null;
                try {
                    in = response.getEntity().getContent();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in,Charset));
                    String line = null;
                    while ((line = reader.readLine()) != null) {
                        buffer.append(line);
                    }

                } finally {
                    if (in != null)
                        in.close();
                }

                return buffer.toString();
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    public static String httpsRequest(String requestUrl, String requestMethod,String output){
        try {
            URL url = new URL(requestUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setRequestMethod(requestMethod);
            if (null != output){
                OutputStream outputStream = connection.getOutputStream();
                outputStream.write(output.getBytes("UTF-8"));
                outputStream.close();
            }
            //从输出流返回内容
            InputStream inputStream = connection.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null){
                buffer.append(str);
            }
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            connection.disconnect();
            return buffer.toString();
        }catch (Exception e){
            e.printStackTrace();
        }
        return "";
    }
}

3.导入API对应的SDK

Tip:这个听说好像是可以导maven坐标,不知道是不是一个东西,我没有研究,那种应该比这个厉害-_-
导入之后是这个样子的

下载地址:https://pay.weixin.qq.com/wiki/doc/api/micropay_sl.php?chapter=11_1


二、编写controller

1.先获取openid给小程序

@ApiOperation("登录获取openid")
    @RequestMapping(value = "/loginOpenid", method = {RequestMethod.GET})
    public Map<String, Object> longin(@RequestParam("code") String code) {
        String url = this.requestUrl + "?appid=" + WechatConstant.APPID + "&secret=" + WechatConstant.SECRET + "&js_code=" + code + "&grant_type=" + grantType;
        // 发送请求
        String data = HttpUtil.get(url);
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> json = null;
        try {
            json = mapper.readValue(data, Map.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return json;
    }

2.获取请求支付ip地址,传给service

@ApiOperation("支付")
    @RequestMapping(value = "/pay", method = {RequestMethod.GET})
    public Map<String, String> pay(HttpServletRequest request,
                                   @RequestParam("openid") String openid,
                                   @RequestParam("paymoney") String paymoney,//支付金额
                                   @RequestParam("activityid") int activityid) throws Exception {  
        //获取请求ip地址
        this.activity = activityid;   //这里的activityid是我的业务需要,你们按需求写自己的参数就好了
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip.indexOf(",") != -1) {
            String[] ips = ip.split(",");
            ip = ips[0].trim();
        }
        return wxPayService.wxPay(openid, ip, paymoney);
    }

三.编写server

Tip:tip都写在注释里了,认真看主要内容都在这里了。。

@Slf4j
@Service
public class WxPayServiceImpl extends ServiceImpl<WxPayNotifyVOMapper, Wxpaynotifyvo> implements WxPayService {


    @Override
    public Map<String, String> wxPay(String openid, String ip, String paymoney) {
        System.out.println("openid"+openid);
        try {
            //1.拼接下单地址参数,生成商户订单
            Map<String, String> paraMap = new HashMap<String, String>();
            paraMap.put("body", "混读罗的好友圈");  //这里写的是商家的名称,支付的时候会显示
            paraMap.put("openid", openid);       //openid,通过登录获取,在支付的controller里边有。
            paraMap.put("out_trade_no", UUID.randomUUID().toString().replaceAll("-", ""));//订单号,每次都不同
            paraMap.put("spbill_create_ip", ip);  //请求的IP地址
            paraMap.put("total_fee", paymoney);   //支付金额,单位分
            paraMap.put("trade_type", "JSAPI");   //交易类型,小程序支付用JSAPI,其他的可以看微信开发文档
            log.info("paraMap为:" + paraMap);     //打印日志
            log.info("+++++++++++++++++++分割线++++++++++++++++++++");

            //2.通过MyWXPayConfig创建WXPay对象,用于支付请求。调用支付统一下单API
            final String SUCCESS_NOTIFY = "http://59.111.56.123:8090/hunduluo/success";     //支付成功的回调接口,需要自己写一个。(我写的是假的,就是举个例子),这个接口后边补充
            boolean useSanbox = false;  //是否用沙河环境支付API,是的话不会真正扣钱
            MyWXPayConfig config = new MyWXPayConfig();
            WXPay wxPay = new WXPay(config, SUCCESS_NOTIFY, false, useSanbox);

            //3、fillRequestDate会将上述参数用key=value形式和mchkey一起加密为sign,验证地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi/php?chapter=20_1;
            Map<String, String> map = wxPay.unifiedOrder(wxPay.fillRequestData(paraMap), 15000, 15000);

            //4、发送post请求“统一下单接口”(https://api.mch.weixin.qq.com/pay/unifiedorder),返回预支付id:prepay-id
            String prepayId = (String) map.get("prepay_id");
            log.info("xmlStr为:" + map);
            log.info("===================分割线=================");
            Map<String, String> payMap = new HashMap<String, String>();
            payMap.put("appId", WechatConstant.APPID);    //小程序的appid
            payMap.put("timeStamp", WXPayUtil.getCurrentTimestamp() + "");    //时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
            payMap.put("nonceStr", WXPayUtil.generateNonceStr());   //随机字符串,长度为32个字符以下。
            //是否用沙河,我这里不用
            if (useSanbox) {
                payMap.put("signType", WXPayConstants.MD5);
            } else {
                payMap.put("signType", WXPayConstants.HMACSHA256);
            }
            payMap.put("package", "prepay_id=" + prepayId);

            //5.通过appid,timeStamp,nonceStr,signType,package及圣湖密钥进行key=value形式拼接并加密
            String paySign = null;
            if (useSanbox) {
                paySign = WXPayUtil.generateSignature(payMap, WechatConstant.MCH_KEY, WXPayConstants.SignType.MD5);
            } else {
                paySign = WXPayUtil.generateSignature(payMap, WechatConstant.MCH_KEY, WXPayConstants.SignType.HMACSHA256);
            }
            payMap.put("paySign", paySign);

            //6、将着六个参数穿给前端,也就是小程序,用这些数据去调用微信官方wx.requestPayment支付请求
            log.info("payMap:" + payMap);
            return payMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

四.小程序端调用requestPayment

Tip:小程序接收后台传来的那几个参数,就是server里最后传回去那几个,进行wx.requestPayment

function(res){
        wx.requestPayment({
          nonceStr: res.data.nonceStr,
          package: res.data.package,
          paySign: res.data.paySign,
          timeStamp: res.data.timeStamp,
          signType:res.data.signType,
          //支付成功
          success (res) {
            //业务逻辑
          },
          //支付失败
          fail (res) {
            //业务逻辑
           },
           complete(res){
             
           }
        })
      }

补充

这里是支付成功的回调接口

Tip:为了给微信服务器一个return_code SUCCESS,否则它会每隔一段时间请求一次,并且用这个接口将支付信息Wxpaynotifyvo存起来,以后退款会用到。

@ApiOperation("支付成功后回调")
    @RequestMapping(value = "/success", produces = MediaType.TEXT_PLAIN_VALUE)
    public String success(HttpServletRequest request, @RequestBody Wxpaynotifyvo param) throws Exception {
        System.out.println("param:      "+param);
        param.setActivityid(this.activity);
        param.setTransactionid(param.getTransaction_id());
        param.setTotalfee(param.getTotal_fee());
        int insert = this.wxPayService.putWxPayNotifyVO(param);
        HashMap<String, String> result = new HashMap<String, String>();
        if ("SUCCESS".equals(param.getReturn_code())) {
            result.put("return_code", "SUCCESS");
            result.put("return_msg", "OK");
        }
        log.info(String.valueOf(param));
        return WXPayUtil.mapToXml(result);
    }

Tip:这个controller对应的server不写了(就不两行),把Wxpaynotifyvo内容写一下。

@Data
@XmlRootElement
@NoArgsConstructor
@AllArgsConstructor
public class Wxpaynotifyvo {
    @TableId(value = "wxpaynotifyvo")
    private int wxpaynotifyvo;
    private String appid;
    private String attach;
    private String bank_type;
    @TableField(value = "fee_type")
    private String fee_type;
    private String is_subscribe;
    private String mch_id;
    private String nonce_str;
    private String openid;
    private String out_trade_no;
    private String result_code;
    private String return_code;
    private String sign;
    private String time_end;
    private String total_fee;
    private String coupon_fee;
    private String coupon_count;
    private String coupon_type;
    private String coupon_id;
    private String trade_id;
    private String transaction_id;
    private int activityid;
    private String transactionid;
    private String totalfee;
}

Tip:讲道理,逻辑有点乱,可能看不懂,大二暑假做的东西,寒假才写博客记录头秃。真的是想不起来了,一边回忆一边质疑T_T。主要是记录一下,第一次写支付的时候也研究了好久,但最后我确实实现了支付和退款功能。(退款下篇写)希望对读者有帮助。欢迎纠错~

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值