package com.itemutils.weixin.pay;
import com.alibaba.fastjson.JSON;
import com.itemutils.http.apache.UseHttp;
import com.itemutils.weixin.pay.official.WXPayConstants;
import com.itemutils.weixin.pay.official.WXPayUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Inet4Address;
import java.util.HashMap;
import java.util.Map;
/**
-
参考地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_2
-
-
工具类下载https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
-
@Author zbf
-
@DATA 2019/9/2 19:02
-
@ClassIntroduction 微信JSAPI支付
*/
public class JSAPIUtil {/**
- 微信公众号中需要配置
- 开发/基本配置/IP白名单 (这里的ip是指项目运行的服务器)
- 设置/公众号设置/功能设置/业务域名
- 设置/公众号设置/功能设置/JS接口安全域名
- 设置/公众号设置/功能设置/网页授权域名
-
- 商户号中需要开通jsapi支付 并且需要绑定微信公众号
-
- 首先需要获得微信公众号的:开发/基本配置/开发者ID(AppID)
-
- 注意:微信开发过程中注意参数的顺序:顺序不对,授权页面将无法正常访问
*/
private static final String APP_ID = “”;
private static final String APP_SECRET = “”;
//商户号
private static final String MCH_ID = “”;
//密钥
private static final String MCH_KEY = “”;
/**
-
第一步
-
-
-
因为需要用户同意,所以在前端访问:用户同意授权,获取code
-
前端访问通过(APP_ID)获取到code: (APP_ID)
-
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
-
@param code
-
@return access_token 也就是openId
*/
public static Map<String, String> getOpenId(String code) {String url = “https://api.weixin.qq.com/sns/oauth2/access_token?appid=” + APP_ID +
“&secret=” + APP_SECRET +
“&code=” + code +
“&grant_type=authorization_code”;
//自己封装的get请求
String ticketStr = UseHttp.get(url);
String openid = JSON.parseObject(ticketStr).getString(“openid”);
Map<String, String> hashMap = new HashMap<>();
hashMap.put(“openId”, openid);
return hashMap;
}
/**
-
第二步:发送请求
-
微信支付 jsapi支付
-
@param openId openId是必需的
-
@param money 需要支付的金额
-
@return
-
@throws Exception
*/
public static Map<String, String> postOrderId(String openId, String money) throws Exception {Map<String, String> hashMap = new HashMap<>();
hashMap.put(“appid”, APP_ID);//微信公众号的app_id
hashMap.put(“mch_id”, MCH_ID);//商户号hashMap.put(“nonce_str”, WXPayUtil.generateNonceStr());//随机字符串,长度要求在32位以内。推荐随机数生成算法
hashMap.put(“sign_type”, WXPayConstants.MD5);//不是必需有默认值 签名类型,默认为MD5,支持HMAC-SHA256和MD5。
/**- 使用场景 支付模式 商品字段规则 样例 备注
- PC网站 扫码支付 浏览器打开的网站主页title名 -商品概述 腾讯充值中心-QQ会员充值
- 微信浏览器 公众号支付 商家名称-销售商品类目 腾讯-游戏 线上电商,商家名称必须为实际销售商品的商家
- 门店扫码 公众号支付 店名-销售商品类目 小张南山店-超市 线下门店支付
- 门店扫码 扫码支付 店名-销售商品类目 小张南山店-超市 线下门店支付
- 门店付款码 付款码支付 店名-销售商品类目 小张南山店-超市 线下门店支付
- 第三方手机浏览器 H5支付 浏览器打开的移动网页的主页title名-商品概述 腾讯充值中心-QQ会员充值
- 第三方APP APP支付 应用市场上的APP名字-商品概述 天天爱消除-游戏充值
*/
hashMap.put(“body”, “活动礼包”);//商品简单描述,该字段请按照规范传递(产品名称)
/**
- 商户支付的订单号由商户自定义生成,仅支持使用字母、数字、中划线-、下划线_、竖线|、星号*这些英文半角字符的组合,
- 确定唯一性
/
hashMap.put(“out_trade_no”, WXPayUtil.generateNonceStr());//商户订单号 32
hashMap.put(“fee_type”, “CNY”);//不是必需 符合ISO 4217标准的三位字母代码,默认人民币:CNY,详细列表请参见货币类型
/* - 单位为分
/
hashMap.put(“total_fee”, money);//订单总金额,单位为分,详见支付金额88
hashMap.put(“spbill_create_ip”, Inet4Address.getLocalHost().getHostAddress());//支持IPV4和IPV6两种格式的IP地址。用户的客户端IP
/* - trade_type=JSAPI时(即JSAPI支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识。openid如何获取,
- 可参考【获取openid】。企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid,再调用【企业号userid转openid接口】进行转换
/
hashMap.put(“openid”, openId);//不是必需
hashMap.put(“notify_url”, “http://www.xxx.com/restult”);//异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
/* - JSAPI -JSAPI支付
- NATIVE -Native支付
- APP -APP支付
- 说明详见参数规定
*/
hashMap.put(“trade_type”, “JSAPI”);
//以下信息不是必需信息
// ************************************************************************************************************************************hashMap.put(“device_info”, “”);//设备号 不是必需
hashMap.put(“detail”, “”);//不是必需 商品详情 6000
hashMap.put(“attach”, “”);//不是必需 附加数据 127
hashMap.put(“time_start”, “”);//不是必需 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
hashMap.put(“time_expire”, “”);//不是必需 交易结束时间
hashMap.put(“goods_tag”, “”);//不是必需 订单优惠标记
hashMap.put(“product_id”, “”);//不是必需 商品ID trade_type=NATIVE时,此参数必传。此参数为二维码中包含的商品ID,商户自行定义
hashMap.put(“limit_pay”, “”);//指定支付方式上传此参数no_credit–可限制用户不能使用信用卡支付
hashMap.put(“receipt”, “”);//不是必需 电子发票入口开放标识
/**- -门店id id 否 String(32) SZTX001 门店编号,由商户自定义
- -门店名称 name 否 String(64) 腾讯大厦腾大餐厅 门店名称 ,由商户自定义
- -门店行政区划码 area_code 否 String(6) 440305 门店所在地行政区划码,详细见《最新县及县以上行政区划代码》
- -门店详细地址 address 否 String(128) 科技园中一路腾讯大厦 门店详细地址 ,由商户自定义
/
hashMap.put(“scene_info”, “存放对象”);//不是必需
//*************************************************************************************************************************************
/** - 最后进行加密
/
hashMap.put(“sign”, WXPayUtil.generateSignature(hashMap, MCH_KEY));//通过签名算法计算得出的签名值,详见签名生成算法
String data = WXPayUtil.mapToXml(hashMap);
/* - 订单提交地址
*/
String url = “https://api.mch.weixin.qq.com/pay/unifiedorder”;
String result = UseHttp.post(url, data);
Map<String, String> stringStringMap = WXPayUtil.xmlToMap(result);
//请求结果信息返回
HashMap<String, String> results = new HashMap<>();
if (stringStringMap.get(“stringStringMap”).equals(“SUCCESS”)) {
//处理相关的业务逻辑
results.put(“status”, “SUCCESS”);
} else {
throw new RuntimeException(“支付失败!请重新支付…”);
}
return results;
}
/**
-
第三步:一定要将信息返回给微信端
-
回调接口
-
@param request 获得的信息
-
@param response 响应信息
*/
public static void postRestult(HttpServletRequest request, HttpServletResponse response) {
try {
InputStream inputStream = request.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String result = “”;
String s;
while ((s = reader.readLine()) != null) {
result = result + s.trim();
}
reader.close();
Map<String, String> map = WXPayUtil.xmlToMap(result);
//订单号 回调成功以后,才能算真正的支付成功
String out_trade_no = map.get(“out_trade_no”);//通知微信端已经支付成功,一定要做 String xml = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"; response.getWriter().write(xml);
} catch (Exception e) {
e.printStackTrace();
}
}
}