JSAPI支付介绍:
JSAPI支付是用户在微信中打开商户的H5页面,商户在H5页面通过调用微信支付提供的JSAPI接口调起微信支付模块完成支付。
应用场景有:
◆ 用户在微信公众账号内进入商家公众号,打开某个主页面,完成支付。
◆ 用户的好友在朋友圈、聊天窗口等分享商家页面连接,用户点击链接打开商家页面,完成支付。
◆ 将商户页面转换成二维码,用户扫描二维码后在微信浏览器中打开页面后完成支付。
这里使用将商户页面转换成二维码,用户扫描二维码后在微信浏览器中打开页面后完成支付。
一.JSAPI支付前期准备
JSAPI支付需要准备 :公众号和商户号
1.微信支付需要进行申请
2.公众号平台- 网页授权 和JS授权
网页授权
JS授权
3.微信支付商户平台-支付授权目录
**ps:出现添加后页面没有反应。**
1.添加支付目录后,按流程正确输入操作密码后。界面会跳回来,这个时候发现并没有添加成功。
2.接下来不要刷新网页,也不要点击别的模块。再添加一次刚才添加的目录。
3.这个时候,你会发现不用输入操作密码了,然后,目录也添加成功了
支付授权目录
ps:最好选择为 https//
二、获取主要参数
微信商户:
Appid:xxxx;
mch_id:xx
APi:xxx;
公众号:
Appid: xx
AppSecret:xx
1、商户号平台参数
商户号Appid的获取方法:微信支付商户后台(pay.weixin.qq.com) -> 【账户中心】->【账户设置-商户信息】->微信支付商户号。
微信支付商户号mch_id
商户密匙API:在微信支付商户平台(pay.weixin.qq.com),进入【账户中心】->【账户设置-API安全】->【设置密钥】,自助设置32位API密钥即可。如下图所示:
*注意:请事先将需设置的密钥用文档记录,设置成功后不支持查看,只支持修改重设。
2.公众号参数
Appid:xx
AppSecret:xx
ps:注意设置服务器白名单。
3、配置完API密钥后,请记得在【产品中心 – JSAPI支付】,开通JSAPI支付。
ed:微信支付开通完成。
三、开始开发
微信支付-》微信支付回调-》验证回调的签名支付信息-》支付成功
微信退款-》微信退款回调-》解密回调信息-》退款成功
1.获取用户的openid
这里使用的是:网页授权获取用户openid
具体而言,网页授权流程分为四步:
- 引导用户进入授权页面同意授权,获取code
- 通过code换取网页授权access_token(与基础支持中的access_token不同)
- 如果需要,开发者可以刷新网页授权access_token,避免过期
- 通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
网站链接转二维码地址:https://cli.im/
//TODO 回调接口
@RequestMapping("/gzh")
public class GzhHtmlController {
@RequestMapping(value = "/queryFee")
public ModelAndView queryFee(String code) {
ModelAndView mv = new ModelAndView();
String openid=openidUtil.htmlGetOpenid(code);
mv.addObject("openid",openid);
mv.setViewName("gzh/index");
return mv;
}
}
getOpenid方法:
/**
*
* @param appid 公众号appid
* @param appSecret:xx 公众号appsecret
* @param code
* @return
*/
public String getOpenid(String appid, String appSecret:xx, String code) {
try {
RestTemplate restTemplate = new RestTemplate();
String url = String.format(
"https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
appid, appSecret:xx, code);
String res = restTemplate.getForObject(url, String.class);
ObjectMapper mapper = new ObjectMapper();
@SuppressWarnings("rawtypes")
HashMap m = mapper.readValue(res, HashMap.class);
String openid = (String) m.get("openid");
return openid;
} catch (Exception e) {
e.printStackTrace();
return null;
}
地址中的参数:
1.appid=公众号的appid
2.redirect_uri= https:域名/项目名/gzh/queryFee 地址需使用urlEncode 对链接进行处理
3.response_type=code&scope=snsapi_base&state=123#wechat_redirect 默认
2.支付代码
https://blog.csdn.net/daotiao0199/article/details/85284038
3.退款
回调解密:
AESUtil.java
package cn.edu.cqu.paymentservice.utils.jmutil;
import cn.edu.cqu.paymentservice.utils.wxpay.WXPayUtil;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Map;
/**
* @author jing.huang
* @function
* @date 2018年1月10日
* @version
*/
public class AESUtil {
/**
* 密钥算法
*/
private static final String ALGORITHM = "AES";
/**
* 加解密算法/工作模式/填充方式
*/
private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS5Padding";
/**
* 生成key
*/
private static SecretKeySpec key = new SecretKeySpec(
MD5.MD5Encode("api密匙").toLowerCase().getBytes(), ALGORITHM);
/**
* AES加密
*
* @param data
* @return
* @throws Exception
*/
public static String encryptData(String data) throws Exception {
// 创建密码器
Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
// 初始化
cipher.init(Cipher.ENCRYPT_MODE, key);
return Base64Util.encode(cipher.doFinal(data.getBytes()));
}
/**
* AES解密
*
* @param base64Data
* @return
* @throws Exception
*/
public static String decryptData(String base64Data) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(Base64Util.decode(base64Data)));
}
public static void main(String[] args) throws Exception {
String A = "<xml><return_code>SUCCESS</return_code><appid><![CDATA[wx6ee8f89d58e108ad]]></appid><mch_id><![CDATA[1389309602]]></mch_id><nonce_str><![CDATA[6c1060a9bce50ae1dbb99185a3addc2a]]></nonce_str><req_info><![CDATA[5HOIoMIqBE6kfuOI96rWcq7egyPUHd0wPscqU2ADA/TKchAZIbUQQbGGPbLvaBq2q3p4oyoowc53seV/I+F977v+IA8C7BAExbftBVIhuKo9C3ii6j8gDmkB3fuc31lLdcQoQqiZWO46voDH+Uc6P4+Q3chOqlZlKITQZ+4YxCe898UBCymgQNEO9LNW3gR6s1cIykfMjgeGjUJ89ugdI3tAIs9nEXvWQmzFSKLurFTn6Juj5B6lf4TwpgKzIZxS6zAM5G3opUU0JeoBwMUyUet49w3yoR+HkvPZr1SWiO5ufXYF41b1IzQh9TBL70byL9/o1s+rxTefOY4JRvbMkN139xoXBe1OWH0cJdux56J1XwV4bdiuk0Eh+FxGtABBzEJxZi5w5nara5VRRka+t+B2MSI0fyOwcYZGJEtMKkQlC1MvST6ht45S/midObIY49uCOuq92VuC/HrdmM88Ge881h4T4ccM4ViLTFW2NrKYxFIDFEp3iwhMWY1FpX7A5pIf1i+FLPUlnp9fvqMurG1gYmc61Etvz6HHlohrS+4uItlCLDkJ/O3ZMHFffmta6J2sxOhMOMq3mkkd8LGYan7InznOWQY/XiAxTO/Txxe3s2RJrbBn+BrmQa3Y2Ps2q5BcxMstH0Ln5rV3qG43cpI2zmVtfL0SFBAKn2IB4AdyaiARp8fwPM+PIt7jd215MDTOw5ac4ZppZ6GoS7jUORIO6DVyBlUwfkkIT8fyDutVtp2NZv4X+9rEpp9nH9hgAUU4OdbFVicv3TgLaNJjzpqIu0114c6mVwVavB6ah37+Qyg2PTvTPcGL3+vqXkfW7jnqc/hRBXAQBITyhAIiZnUV8XsU3pFNvDithFqAOBJiQok0JfdPO8uQSIinxuOyTZsnqBpG/1qSeYrzmQKWJda+TKsoBwlsHG+3oXLITyZ416vQJX6Bvof/5tpofuqekVl6GepRkz6ADkwEet/A7Jsp7+KrQwMyF4cWVra69loNJhpnji4qVTHMbPuyStLfngjzmZdA/aL5qvFYRnAIhOEm9WARwyw9QDY01hdleedm6yghd9FRZam7i9cJIA5q]]></req_info></xml>\n";
Map<String, String> map = WXPayUtil.xmlToMap(A); //微信sdk工具方法
String req_info = map.get("req_info");
// 解密 req_info
String B = AESUtil.decryptData(req_info);
System.out.println(B);
Map<String, String> mapInfo = WXPayUtil.xmlToMap(B);
System.out.println(mapInfo.get("refund_status"));
// 加密B
// System.out.println(encryptData(B));
}
}
Base64Util.java
package cn.edu.cqu.paymentservice.utils.jmutil;
import java.util.Base64;
/**
* @author jing.huang
* @function jdk8支持
* @date 2018年1月10日
* @version
*/
public class Base64Util {
public static byte[] decode(String encodedText){
final Base64.Decoder decoder = Base64.getDecoder();
return decoder.decode(encodedText);
}
public static String encode(byte[] data){
final Base64.Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(data);
}
}
MD5.java
package cn.edu.cqu.paymentservice.utils.jmutil;
import java.security.MessageDigest;
public class MD5 {
private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "a", "b", "c", "d", "e", "f"};
/**
* 转换字节数组为16进制字串
* @param b 字节数组
* @return 16进制字串
*/
public static String byteArrayToHexString(byte[] b) {
StringBuilder resultSb = new StringBuilder();
for (byte aB : b) {
resultSb.append(byteToHexString(aB));
}
return resultSb.toString();
}
/**
* 转换byte到16进制
* @param b 要转换的byte
* @return 16进制格式
*/
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n = 256 + n;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
/**
* MD5编码
* @param origin 原始字符串
* @return 经过MD5加密之后的结果
*/
public static String MD5Encode(String origin) {
String resultString = null;
try {
resultString = origin;
MessageDigest md = MessageDigest.getInstance("MD5");
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
} catch (Exception e) {
e.printStackTrace();
}
return resultString;
}
}