微信v3版本-native支付和支付宝支付接口对接

背景:

​ 由于公司业务需要想把支付宝和微信整合在一起,做一个统一的支付模块。其中经过了各种坑,因为之前也没搞过这个。所以这里记录下,同时,也让后面做相同业务的小伙伴们少走一些弯路。

支付宝

​ 接口:alipay.trade.precreate(统一收单线下交易预创建

​ 接口描述: 收银员通过收银台或商户后台调用支付宝接口,生成二维码后,展示给用户,由用户扫描二维码完成订单支付。 这里我要讲下,我们使用这个功能的前提是你的商户账号已经开通“当面付”功能;

这里我们不在贴出参数和响应参数的信息,这些信息在 alipay.trade.precreate(统一收单线下交易预创建 中都有很详细的介绍。我传递相应的参数就可得到一个二维码链接,前端或者后端都可以根据这个链接进行生成二维码;

这里有几个小细节需要注意:

  1. total_amount (订单总金额)为0,是不会返回二维码链接的;其实文档上也有写到,订单金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] ;

  2. notify_url(回调地址),这里的回调地址必须是外网可以直接访问的,本地想测试的话也可以内外网穿透下;这里推荐一个好用的工具utools,下载一个内外网穿透工具就行了;

  3. timeout_express 和 qr_code_timeout_express的区别:官网的详细介绍, 点击查看,这里要说下沙箱和真实商户账号的区别,沙箱中我们扫过码会在支付宝app上有一笔未支付订单信息,过期后还是可以继续支付,但是真实的商户账号,扫码后也会在app有一笔订单,但是在支付时间过期后,就不能再继续支付了;

  4. 沙箱环境回调通知和真实商户账号的回调通知的区别:

    1. 回调通知的次数不同: 沙箱只回调一次(偶尔还不回调,这里也是一个坑);真实的是会多次尝试的,所以可以很放心的使用;
    2. 回调通知的触发条件不同: 沙箱在用户扫码和支付完成都会触发回调通知,通知的状态码分别为:WAIT_BUYER_PAY(交易创建)、TRADE_SUCCESS(交易成功)
  5. 沙箱环境返回的qr_code和真实商户返回的qr_code的区别: 沙箱返回的qr_code是不带反斜杠的’\‘,但是真实的商户账号环境返回的qr_code是带反斜杠的,所以如果有小伙伴是在沙箱写完后再对接真实商户账号的话,就非常容易忽略这一点,如果没有处理掉这个反斜杠,生成的二维,用支付宝app扫码就是会是一片空白;这个时候就是一脸懵逼;

微信支付:

​ 背景:

​ 这里要说下,微信支付时分为v3和v2两个版本的,这里选用的v3版本,但是网上关于v3版本的demo很少,v3和v2版本的差别很大,所以路途很艰难;

​ 接口: 普通支付(直连模式)/ Native下单API)

​ 接口描述:除付款码支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付。

​ 在 普通支付(直连模式)/ Native下单API)也有很详细的字段介绍,下面我要将的就是几个注意的点:

  1. 微信支付设置GET/POST 请求头和请求体要设置相应的格式:

    /**
         * 请求支付端(GET)
         *
         * @param url 请求地址
         * @return 请求结果
         */
        public static String sendGet(String url) {
            HttpGet request = new HttpGet(url);
          	// 这个header是必须设置
            request.addHeader("Accept", "application/json");
            request.addHeader("Content-Type", "application/json");
            return builder(request);
        }
    
        /**
         * 请求支付端(POST)
         *
         * @param url  请求地址
         * @param json 参数
         * @return 请求结果
         */
        public static String sendPost(String url, JSONObject json) {
            String res = null;
            try {
                HttpPost request = new HttpPost(url);
                // 这个header和entity是必须设置 
                request.addHeader("Accept", "application/json");
                request.addHeader("Content-Type", "application/json");
                request.setEntity(new StringEntity(json.toString(), ContentType.create("application/json", "utf-8")));
                res = builder(request);
            } catch (UnsupportedCharsetException e) {
                LOG.error("调用WXPay接口失败:", e);
            }
            return res;
        }
    
    
        /**
         * 构建微信支付请求
         *
         * @param request HttpURL
         * @return HttpResponse
         */
        private static String builder(HttpUriRequest request) {
            CloseableHttpClient client = null;
            String res = null;
            try {
                // 获取支付客户端
                client = WechatPayHttpClientBuilder.create()
                        .withMerchant(MERCHANT_ID, serialNo, privatekey)
                        .withValidator(response -> true)
                        .build();
                // 处理请求结果
                HttpResponse response = client.execute(request);
                if (response.getEntity() != null) {
                    res = EntityUtils.toString(response.getEntity());
                    EntityUtils.consume(response.getEntity());
                }
            } catch (Exception e) {
                LOG.error("调用WXPay接口失败:", e);
            } finally {
                if (client != null) {
                    try {
                        client.close();
                    } catch (IOException e) {
                        LOG.error("调用WXPay接口失败:", e);
                    }
                }
            }
            return res;
        }
    

    这里必须要在request中设置相应的header和entity请求格式;如果没有设置,请求过去微信也会返回给你一个二维码链接,但是这个链接生成二维码后,使用微信app扫码就会出现不停的跳转;这也是很坑的地方;

    1. 注意传给微信的对象的类型;例如:总金额(total)是int类型,本人就是传了一个BigDecimal也是可以拿到二维链接,但是同样出现了使用微信app扫码不停的跳转,最终弹出系统繁忙的信息;

    微信回调通知

    1. notify_url参数设置,必须为https协议。如果链接无法访问,商户将无法接收到微信通知。 通知url必须为直接可访问的url,不能携带参数。示例: “https://pay.weixin.qq.com/wxpay/pay.action”

      这个notify_url 一个商户账号只能设置一个回调地址;

    2. 设置完回调地址就可以正常接收到微信的回调,我们可以在request中读取到响应的信息;

    private String getWxParams(HttpServletRequest request) {
            InputStream inStream = null;
            ByteArrayOutputStream outStream = null;
            String result = null;
            try {
                inStream = request.getInputStream();
                outStream = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = inStream.read(buffer)) != -1) {
                    outStream.write(buffer, 0, len);
                }
                result = new String(outStream.toByteArray(), "utf-8");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != inStream) {
                        inStream.close();
                    }
                    if (null != outStream) {
                        outStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
            }
            return result;
        }
    

    这里拿到是加密的json对象,具体的信息在resource中;例如:

    {
        "id":"EV-2018022511223320873",
        "create_time":"2015-05-20T13:29:35+08:00",
        "resource_type":"encrypt-resource",
        "event_type":"TRANSACTION.SUCCESS",
        "resource" : {                       
            "algorithm":"AEAD_AES_256_GCM",
            "ciphertext": "...",
            "nonce": "...",
            "associated_data": ""
        },
    	"summary":"支付成功"
    }
    

    下面我们就要在商户平台上设置的APIv3密钥【微信商户平台—>账户设置—>API安全—>设置APIv3密钥】,拿到一个key;然后通过微信提供的工具类(AesUtil)进行解密,大家可以具体看下,这里提供一个解密demo

    //  PayConstants.API_V3 就是上面我们设置APIv3拿到的key
    AesUtil aesUtil = new AesUtil(PayConstants.API_V3.getBytes(StandardCharsets.UTF_8));
    // 解密回调信息  associatedData,nonce,ciphertext 分别是上面resource中的对应值
    String decryptToString = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
    

    通过上面的解密后就可以拿到真实的回调支付信息了,例如:

    {
    	"transaction_id": "1217752501201407033233368018",
    	"amount": {
    		"payer_total": 100,
    		"total": 100,
    		"currency": "CNY",
    		"payer_currency": "CNY"
    	},
    	"mchid": "1230000109",
    	"trade_state": "SUCCESS",
    	"bank_type": "CMC",
    	"promotion_detail": [{
    		"amount": 100,
    		"wechatpay_contribute": 0,
    		"coupon_id": "109519",
    		"scope": "GLOBALSINGLE",
    		"merchant_contribute": 0,
    		"name": "单品惠-6",
    		"other_contribute": 0,
    		"currency": "CNY",
    		"type": "CASHNOCASH",
    		"stock_id": "931386",
    		"goods_detail": [{
    			"goods_remark": "商品备注信息",
    			"quantity": 1,
    			"discount_amount": 1,
    			"goods_id": "M1006",
    			"unit_price": 100
    		}, {
    			"goods_remark": "商品备注信息",
    			"quantity": 1,
    			"discount_amount": 1,
    			"goods_id": "M1006",
    			"unit_price": 100
    		}]
    	}, {
    		"amount": 100,
    		"wechatpay_contribute": 0,
    		"coupon_id": "109519",
    		"scope": "GLOBALSINGLE",
    		"merchant_contribute": 0,
    		"name": "单品惠-6",
    		"other_contribute": 0,
    		"currency": "CNY",
    		"type": "CASHNOCASH",
    		"stock_id": "931386",
    		"goods_detail": [{
    			"goods_remark": "商品备注信息",
    			"quantity": 1,
    			"discount_amount": 1,
    			"goods_id": "M1006",
    			"unit_price": 100
    		}, {
    			"goods_remark": "商品备注信息",
    			"quantity": 1,
    			"discount_amount": 1,
    			"goods_id": "M1006",
    			"unit_price": 100
    		}]
    	}],
    	"success_time": "2018-06-08T10:34:56+08:00",
    	"payer": {
    		"openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
    	},
    	"out_trade_no": "1217752501201407033233368018",
    	"appid": "wxd678efh567hg6787",
    	"trade_state_desc": "支付失败,请重新下单支付",
    	"trade_type": "MICROPAY",
    	"attach": "自定义数据",
    	"scene_info": {
    		"device_id": "013467007045764"
    	}
    }
    
    1. 由于微信通知规则,即用户在接收并处理完回调信息后,要相应的返回一个应答信息;不然微信会认为商户接收通知失败,微信会在一定时间内策略定期重发通知,通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m);通知应答就是返回一个json字符串,例如:

       /**
           *  微信-回调请求-成功
           */
          public static final String RETURN_WECHAT = "{   \n" +
                  "    \"code\": \"SUCCESS\",\n" +
                  "    \"message\": \"交易成功\",\n" +
                  "}";
      		/**
           * <p>响应微信回调通知</p >
           *
           * @param response
           */
          public void responseNotification(HttpServletResponse response) {
              response.setCharacterEncoding("UTF-8");
              response.setContentType("application/xml; charset=utf-8");
              PrintWriter out = null;
              try {
                  out = response.getWriter();
                  out.print(PayConstants.RETURN_WECHAT);
                  out.close();
                  logger.info("响应微信回调通知成功, 无需重复回调");
              } catch (IOException e) {
                  logger.info("响应微信回调通知报错");
                  e.printStackTrace();
              }
          }
      
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值