使用场景:
在微信公众号里面做一个网页链接,带有微信支付的功能。
这里用的是html+springboot
接入前准备:
准备好一个公众号,我用的公众号是公司申请的。有商户号的。
然后看官方文档,准备好所需步骤。
准备好所需接入前准备-JSAPI支付 | 微信支付服务商平台文档中心
做了一个简单的前端页面,附源码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link rel="icon" type="image/png" href="img/logo.ico" sizes="16x16">
</head>
<body style="max-width: 400px;min-height:300px;margin: 0 auto;border: 1px solid wheat;">
<button id="payBt">支付5元</button>
<input id="code" value="">
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script src="js/jquery-3.4.1.min.js"></script>
<script>
$(document).ready(function () {
// sessionStorage.setItem("openid","oJq4-5vrCjeeMz36PQkCK57Js0bo");
if (sessionStorage.getItem("openid") && sessionStorage.getItem("openid") !== "undefined") {
return false;
}
alert("----没有openid----");
var code = getUrlParam('code'); // 截取路径中的code,如果没有就去微信授权,如果已经获取到了就直接传code给后台获取openId
var local = window.location.href;
alert(local)
var AppId = 'AppId';
alert(code);
if (code == null || code === '') {
alert("-----开始获取code----");
$.ajax({
type: "POST",
url: "http://192.168.2.55/gbmsDev/api/getCodeUrl",
data: {reUrl: local.split("?")[0]},
dataType: "string",
jsonp: 'callback',
success(data) {
// alert(data);
window.location.href = data;
}, error() {
alert("error");
}
, complete() {
// alert("complete");
}
});
} else {
alert("-----已存在code----");
$("#code").val(code);
getOpenId(code);
}
});
// 去后台获取opendi
function getOpenId(code){
alert("-----开始获取openid----");
alert("/api/getAccessToken");
alert(code);
$.ajax({
type: "POST",
url: "http://192.168.2.55/gbmsDev/api/getAccessToken",
data: {code:code},
dataType: "json",
jsonp: 'callback',
success(data) {
alert(data);
alert(data.openid);
if (data.openid!==undefined &&data.openid!=='' ){
sessionStorage.setItem("openid",data.openid);
alert("设置openid")
}
}, error() {
alert("error");
}
, complete() {
alert("complete");
}
});
}
$("#payBt").on("click", function () {
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
alert("addEventListener");
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
alert("attachEvent");
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
});
// 微信支付
function onBridgeReady(){
var openId = sessionStorage.getItem("openid");
$("#code").val(openId);
if(openId === undefined || openId ===''){
return false;
}
$.ajax({
type: "POST",
url: "http://192.168.2.55/gbmsDev/api/getPayParam",
data: {openId:openId},
dataType: "json",
jsonp: 'callback',
success(data) {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', data,
function(res){
alert(JSON.stringify(res));
if(res.err_msg == "get_brand_wcpay_request:ok" ){
alter("支付成功")
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
}
});
}, error() {
alert("error");
}
, complete() {
// alert("complete");
}
});
}
// 获取地址栏参数
function getUrlParam(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
}
</script>
</body>
</html>
html需要引入:
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
下面的是java的代码:
Controller层:
package com.game.spring.controller;
import com.game.spring.utils.WxPayUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/gbmsDev/api")
public class WxController {
@RequestMapping(value = "/test")
public Object banUser(@RequestParam(value = "state",required = false,defaultValue = "999")Integer state) {
System.out.println(state);
return "suc";
}
@RequestMapping(value = "/getCodeUrl")
public Object getCodeUrl(@RequestParam(value = "reUrl",required = false,defaultValue = "") String reUrl) {
return WxPayUtils.getCodeUrl(reUrl);
}
// 获取getAccessToken 里面包含openid
@RequestMapping(value = "/getAccessToken")
public Object getAccessToken(@RequestParam(value = "code",required = false,defaultValue = "") String code) {
return WxPayUtils.getAccessToken(code);
}
@RequestMapping(value = "/getPayParam")
public Object getPayParam(@RequestParam(value = "openId",required = false,defaultValue = "") String openId) {
return WxPayUtils.getPayParam(openId);
}
}
下面是WxPayUtils的代码:
package com.game.spring.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.*;
public class WxPayUtils {
private static Logger logger = LoggerFactory.getLogger(WxPayUtils.class);
//appId 微信公众号里创建的AppId,这里的公众号需要关联到微信支付上,
// 微信公众平台-基本配置-开发者ID(AppID),这个绑定到微信支付-产品中心-AppID账号管理-关联AppID.这里关联公众号的AppId
public static String appId = "wx3crrrrd4ca";
//微信支付商户号 微信支付-账户中心-商户信息-微信支付商户号.查看
public static String mchId = "1611111996";
// 微信下单回调地址,支付成功后会回调这个地址。
//对应文档 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_5.shtml
public static String callbackUri = "http://www.baidu.com";
//APIv3密钥 在微信支付商户平台-账户中心-API安全里设置,如果设置了不知道,就只有修改
public static String apiV3Key = "2q5w8e3a6s9d1eeeeee8p5M6N9a09";
// 第三方用户唯一凭证密钥,即appsecret 微信公众平台-基本配置-开发者密码(AppSecret) 公众号基本配置里查询
public static String appSecret = "39fb76597aeeeeeeed532471";
// JSApi下单url地址
public static String JSApiPayUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
// 商户私钥 获取对应的签名需要 在微信支付商户平台-账户中心-API安全里设置 养宠物那边复制的
// 对应文档 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/open/pay/chapter2_1.shtml
private static String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC3qbz0hDWoilpKR6vYnyi4q+CCjmQUjZVIO9R9QE3OfU4qVIaUYavTL/QReO9QfMZcU3LN8JrfKBRedleBfObXXVTP9e1FZh078LEYOiI9Kdzipf3NvSzjaJqkweMe7gOaGWj60zUBQyIVN9+kQVIcqA/2oNgq3Pz88fRFaOgV91okPTj4yfE5mVZujsuHN1ov1Bbky+cbd77SIA5Vl0dXDVUB+A7ZSpc3GXeXPxbEUvHeeeeeeebOpZzcVYsnR82BfTyKmfKIhvHhQjJpHVwmjw/USxxyblLh7Nc1td7pjoommr9yvBn/knq52mPolurmUTHloOCUyxLFDfUeeeepF9iogaSswxGw6B2qK+Jd1ZOoUHnHS2XrazMwKnG1RuvZ9S6On2/lJBXxjWOcvAeKm0c/Nza3lGxcNFwD6CjiXwo+2cq82L9Y3aBiSJRQKb1";
//商户证书序列号
// 对应文档 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/open/pay/chapter2_1.shtml
private static String mchSerialNo = "23BC1D2F580D5eeeeeeF63D95837CC5";
private static CloseableHttpClient httpClient;
/**
* 第一步:获取code
* 获取用户openID第一步,获取下一个接口的code参数
* 访问这个url后,页面将跳转至 redirect_uri/?code=CODE&state=STATE。
* 跳转的url携带code参数
* 官方文档路径
* https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
* @return url
*/
public static String getCodeUrl(String reUrl) {
String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
url = url.replace("APPID", appId).replace("REDIRECT_URI",URLEncoder.encode(reUrl));
return url;
}
/**
*第二部:获取AccessToken,里面包含openId
* 根据微信公众号的信息获取微信的accessToken
* {"access_token":"61_SiCTd--EFgkIpWZ4MrciAKJN0N_Dv-qdZLnJyh-","expires_in":7870,"refresh_token":"-kpaMn5czZelR87thefNLEC2WK_-","openid":"5vrCjeeMz36PQkCK57bo","scope":"snsapi_base"}
* 官方文档路径
* https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
*/
public static String getAccessToken(String code) {
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
url = url.replace("APPID", appId).replace("SECRET", appSecret).replace("CODE",code);
System.out.println(url);
String res = HttpRequestsUtils.sendGet(url,"");
System.out.println(res);
return res;
}
/**
*第三步:JSAPI下单
* 使用官方开发库请求下单
* @param openId openId
* @return {"prepay_id": "wx2611215250487459928b000"}
* 官方文档路径
* https://pay.weixin.qq.com/wiki/doc/apiv3_partner/open/pay/chapter2_1.shtml
*/
public static String createOrderJSApiV3(String openId) {
payLoading();
//请求URL
HttpPost httpPost = new HttpPost(JSApiPayUrl);
// 请求body参数
StringEntity entity = new StringEntity(JSONObject.toJSONString(buildWxJsApiV3PayJson(openId,0)), "utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
//完成签名并执行请求
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
return EntityUtils.toString(response.getEntity());
} else if (statusCode == 204) {
return "";
} else {
System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
assert response != null;
response.close();
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return "";
}
/**
*第四步:生成支付参数
* 生成支付所需的参数
* @param prepay_id 下单接口返回的参数 预支付交易会话标识
* @return JSONObject
* @throws Exception e
*/
public static JSONObject getTokenWeiXin(String prepay_id){
// 获取随机字符串
String nonceStr = getNonceStr();
// 获取微信小程序支付package
String packagestr = "prepay_id=" + prepay_id;
long timestamp = System.currentTimeMillis() / 1000;
//签名,使用字段appId、timeStamp、nonceStr、package计算得出的签名值
String message = buildMessageTwo(appId, timestamp, nonceStr, packagestr);
//获取对应的签名
String signature = sign(message.getBytes(StandardCharsets.UTF_8));
// 组装返回
JSONObject json = new JSONObject(new JSONObject());
json.put("appId", appId);
json.put("timeStamp", String.valueOf(timestamp));
json.put("nonceStr", nonceStr);
json.put("package", packagestr);
json.put("signType", "RSA");
json.put("paySign", signature);
return json;
}
/**
* 先调用下单,然后调用生成支付参数
* @param openId openId
* @return 支付参数
*/
public static Object getPayParam(String openId){
JSONObject jsonObject = JSON.parseObject(createOrderJSApiV3(openId));
return getTokenWeiXin(jsonObject.getString("prepay_id"));
}
/**
* 构造下单的json(第三步需要)
* @param description 商品描述
* @param amount 订单金额
* @param openId 用户ID onAky51Fojn3NoLrnKwcY
* @return JSONObject
*/
public static JSONObject buildWxJsApiV3PayJson(String description, String amount, String openId) {
//订单金额json
JSONObject amountJson = new JSONObject();
amountJson.put("total", Integer.valueOf(amount));
amountJson.put("currency", "CNY");
//支付者json
JSONObject payerJson = new JSONObject();
payerJson.put("openid", openId);
//基础信息json
JSONObject json = new JSONObject();
// 微信公众号里创建的APPID
json.put("appid", appId);
// 直连商户号 微信支付-账户中心-商户信息查看
json.put("mchid", mchId);
json.put("description", description);
// 商户订单号 自己生成
json.put("out_trade_no", generateNonceStr());
json.put("notify_url", callbackUri);
json.put("amount", amountJson);
json.put("payer", payerJson);
return json;
}
/**
* 根据商品goodsId构造下单的json (第三步需要)
* @return JSONObject
*/
public static JSONObject buildWxJsApiV3PayJson(String openId,Integer goodsId) {
return buildWxJsApiV3PayJson("测试商品", "1", openId);
}
/**
* 构造下单的json 无参数
* @return JSONObject
*/
public static JSONObject buildWxJsApiV3PayJson() {
return buildWxJsApiV3PayJson("测试商品", "1", "123123123124");
}
/**
* 初始化httpClient的通用方法(第三步:JSAPI下单)
* 微信支付官方开发库
* 创建加载商户私钥、加载平台证书
*/
public static void payLoading() {
logger.info("----------初始化httpClient-----------");
// 加载商户私钥(privateKey:私钥字符串)
PrivateKey merchantPrivateKey = PemUtil
.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8)));
// 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
// 初始化httpClient
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier)).build();
}
/**
*看情况调用 ,关闭httpClient客户端
* @throws IOException e
*/
public void after() throws IOException {
httpClient.close();
logger.info("----------关闭httpClient-----------");
}
/**
* 根据微信公众号的信息获取微信的Token(没有使用)
* 官方文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
*/
public static String getToken() {
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
url = url.replace("APPID", appId).replace("APPSECRET", appSecret);
String result = HttpRequestsUtils.sendGet(url,"");
System.out.println(result);
JSONObject jsonObject = JSON.parseObject(result);
return jsonObject.getString("access_token");
}
public static void main(String[] args) {
System.out.println(WxPayUtils.getCodeUrl(""));
// String str = WxPayUtils.getAccessToken();
// System.out.println(str);
}
// 获取随机字符串
public static String getNonceStr() {
return UUID.randomUUID().toString()
.replaceAll("-", "")
.substring(0, 32);
}
// 签名连接成一串
private static String buildMessageTwo(String appId, long timestamp, String nonceStr, String packag) {
return appId + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ packag + "\n";
}
/**
* 生成签名
* 官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml
* @param message message
* @return String
*/
private static String sign(byte[] message) {
Signature sign = null;
try {
sign = Signature.getInstance("SHA256withRSA");
sign.initSign(PemUtil.loadPrivateKey(privateKey));
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
e.printStackTrace();
}
return null;
}
/**
* 随机字符串 订单号
*
* @return
*/
public static String generateNonceStr() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
}
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- json-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.2</version>
</dependency>
<!-- HttpUtils-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.22</version>
</dependency>
<!-- 微信SDK-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.8</version>
</dependency>
<!-- HttpUrl要用-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
<!-- java基础类-->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
到这里就差不多了,每个步骤都有具体官方文档,这个必须得看,因为有些要在微信公众号里设置,有些要在微信支付里设置,弄的地方还是挺多的。
代码写的丑,讲究看吧,又不是不能用。。。
如果有什么问题,欢迎大佬指正,最后如果对你有一点点帮助,麻烦支持一下。
全国寄快递4元起,电影票8.8折。更多优惠关注公众号:【折价寄件】
感谢观看!!!!