1.注册
1.1接入微信支付
https://pay.weixin.qq.com/index.php/apply/applyment_home/guide_normal
1.2微信公众平台(小程序)
https://pay.weixin.qq.com/index.php/apply/applyment_home/guide_normal
2. 小程序微信认证
https://mp.weixin.qq.com/wxamp/basicprofile/index?token=1160814128&lang=zh_CN
进入后找到最下方的:设置-微信认证
需要 300RMB文章来源:https://www.toymoban.com/news/detail-727078.html
3. 微信支付和小程序进行关联
https://pay.weixin.qq.com/index.php/extend/merchant_appid/mapay_platform/account_manage
打开网页
点击 : 产品中心-AppID账号管理-关联AppID文章来源地址https://www.toymoban.com/news/detail-727078.html
//这个是对应项目的文件名称 放在resources下面
wxpay.properties
#appid 小程序id(小程序获取)
v3.appId=XXXXXX
#商户id (微信支付获取)
v3.merchantId=XXXXXXXXX
#小程序appSecret (小程序获取)
v3.appSecret=203732ad918e3dbcd515d2f9a7587529
#证书序列号(微信支付获取) 这个在账号中心API 安全里面获取 地址:https://kf.qq.com/faq/161222NneAJf161222U7fARv.html
v3.merchantSerialNumber=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#证书 放在resources下
v3.certKeyPath=/wxPay/apiclient_key.pem
#apiV3Key 获取地址 https://pay.weixin.qq.com/index.php/core/cert/api_cert#/
v3.apiV3Key=XXXXXXXXXXXXXXXXXXXXXXXXX
//这个回调接口的线上地址 微信要调用的接口 你也用内网穿透
v3.wxnotify=http://XXXXXXXXXXXx
配置实体类
WxPayConfig
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@Data
//如果你的参数写在配置文件里面 就打开这个
//@ConfigurationProperties(prefix = "wx.pay")
@PropertySource("wxpay.properties")
//配置前缀
@ConfigurationProperties(prefix = "v3")
public class WxPayConfig {
//appId
private String appId;
//商户id
private String merchantId;
//appSecret
private String appSecret;
//证书序列号
private String merchantSerialNumber;
//私钥路径
private String certKeyPath;
//商户秘钥
private String apiV3Key;
//回调通知地址
private String wxnotify;
}
/**
* 订单实体类
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class StuOrderBo extends BaseEntity {
/**
* 订单id
*/
private Long id;
/**
* 设备信息
*/
private String deviceInfo;
/**
* 订单号
*/
private String orderNo;
/**
* 三方订单号
*/
private String thirdOrderNo;
/**
* 交易类型
*/
private String transactionType;
/**
* 付款银行
*/
private String paymentBank;
/**
* 订单金额
*/
private BigDecimal orderAmount;
/**
* 支付完成时间
*/
private Date paymentCompletionTime;
/**
* 支付类型 0微信/1支付宝
*/
private String paymentType;
/**
* 0未退款/1已退款
*/
private String isRefund;
/**
* 学生用户id
*/
private Long stuUserId;
/**
* openId
*/
private String openId;
/**
* 微信支付退款单号
*/
private String refundId;
/**
* 退款单号
*/
private String outRefundNo;
}
/**
* 生产订单 10分钟内有效
* @RepeatSubmit()这个注解是防止重复提交的
* StuOrderBo 这必须有 单号和总额 这里是自己的订单还需要微信的订单
*/
//@RepeatSubmit()
@PostMapping("createOrder")
public R<StuOrderBo> createOrder(@Validated(AddGroup.class) @RequestBody StuOrderBo bo) {
//设置订单号
bo.setOrderNo(UUID.randomUUID().toString().replace("-", ""));
//看看业务设置订单的金额
bo.getOrderAmount.multiply(new BigDecimal(100)
//添加金额
bo.setOrderAmount(totalFee);
RedisUtils.setCacheObject( bo.getId(), bo, Duration.ofMinutes(10));
return R.ok(bo);
}
/**
* 更具stuUserId查询订单号
*/
@GetMapping("getByStuUserIdOrder")
public Object getByOpenidOrder(@NotNull(message = "stuUserId不可未空") Long stuUserId) {
return R.ok(RedisUtils.getCacheObject(stuUserId));
}
//这个类是预支付信息
import lombok.Data;
@Data
public class WxPayInfo {
/**
* appid
*/
private String appId;
/**
* 随机字符串
*/
private String timeStamp;
/**
* 签名
*/
private String nonceStr;
/**
* 预支付id
*/
private String prepay_id;
private String packageStr;
/**
* 签名类型
*/
private String signType;
private String paySign;
}
下面是控制器实体类接口
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.time.Duration;
import java.util.*;
/**
* 微信支付相关接口
*
* @author xsk
*/
@RestController
@RequestMapping("/wx/pay")
@Slf4j
public class WxPayController extends BaseController {
private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final Random RANDOM = new SecureRandom();
//配置微信支付
@Autowired
private WxPayConfig wxPayConfig;
//微信专业httpClient
private static CloseableHttpClient httpClient;
//生成签名用
private static Sign sign;
//证书管理器
private static CertificatesManager certificatesManager;
/*
*初始化配置
*/
@PostConstruct
public void init() throws Exception {
//获取商户私钥
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(getFileInputStream(wxPayConfig.getCertKeyPath()));
// 获取证书管理器实例
certificatesManager = CertificatesManager.getInstance();
sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, merchantPrivateKey.getEncoded(), null);
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(wxPayConfig.getMerchantId(), new WechatPay2Credentials(wxPayConfig.getMerchantId(),
new PrivateKeySigner(wxPayConfig.getMerchantSerialNumber(), merchantPrivateKey)), wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
// 从证书管理器中获取verifier
Verifier verifier = certificatesManager.getVerifier(wxPayConfig.getMerchantId());
//用于构造HttpClient
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(wxPayConfig.getMerchantId(), wxPayConfig.getMerchantSerialNumber(), merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier));
httpClient = builder.build();
}
/**
* 使用getResourceAsStream直接从resources根路径下获取文件流
*
* @param path
*/
private InputStream getFileInputStream(String path) {
InputStream in = this.getClass().getResourceAsStream(path);
return in;
}
/**
* 微信支付下单返回预支付id
*/
@PostMapping("/createOrder")
public R createOrder(@RequestBody StuOrderBo bo) throws Exception {
JSONObject obj = new JSONObject();
obj.put("appid", wxPayConfig.getAppId());
obj.put("mchid", wxPayConfig.getMerchantId());
//设置订单过期时间 (选填 ) 一定要设置不然一直不过期
obj.put("time_expire", DateUtils.toExpirationTime(RedisUtils.getTimeToLive(bo.getStuUserId())));
obj.put("description", "这个地方填写商品的信息");
obj.put("out_trade_no", bo.getOrderNo());
//线上地址
obj.put("notify_url", wxPayConfig.getWxnotify());
JSONObject amount = new JSONObject();
//获取订单的金额 微信支付需要的单位是分钱 如果你是元需要*100
amount.put("total", bo.totalFee.intValue());
obj.put("amount", amount);
JSONObject payer = new JSONObject();
//支付者的openId
payer.put("openid", bo.getOpenId());
obj.put("payer", payer);
log.info("请求参数为:" + JSON.toJSONString(obj));
//执行请求
JSONObject jsonResult = sendHttpPost(obj, "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
String prePayId = jsonResult.getString("prepay_id");
if (prePayId == null) {
String message = jsonResult.getString("message");
throw new Exception(message);
}
WxPayInfo wxPayInfo = new WxPayInfo();
wxPayInfo.setAppId(wxPayConfig.getAppId());
String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
wxPayInfo.setTimeStamp(timeStamp);
String nonceStr = IdUtil.fastSimpleUUID();
wxPayInfo.setNonceStr(nonceStr);
wxPayInfo.setPrepay_id(prePayId);
String packageStr = "prepay_id=" + prePayId;
wxPayInfo.setPackageStr(packageStr);
wxPayInfo.setSignType("RSA");
String signString = wxPayConfig.getAppId() + "\n" + timeStamp + "\n" + nonceStr + "\n" + packageStr + "\n";
wxPayInfo.setPaySign(Base64.encode(sign.sign(signString.getBytes())).toString());
//可以进行一些操作
return R.ok(wxPayInfo);
}
/**
* 支付回调通知处理
*
* @param
* @return
* @throws
*/
@PostMapping("/notify/order")
public void parseOrderNotifyResult(HttpServletRequest servletRequest) {
String timeStamp = servletRequest.getHeader("Wechatpay-Timestamp");
String nonce = servletRequest.getHeader("Wechatpay-Nonce");
String signature = servletRequest.getHeader("Wechatpay-Signature");
String certSn = servletRequest.getHeader("Wechatpay-Serial");
// String stuUserId= "";
try (BufferedReader reader = new BufferedReader(new InputStreamReader(servletRequest.getInputStream()))) {
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
String obj = stringBuilder.toString();
log.info("回调数据:{},{},{},{},{}", obj, timeStamp, nonce, signature, certSn);
Verifier verifier = certificatesManager.getVerifier(wxPayConfig.getMerchantId());
String sn = verifier.getValidCertificate().getSerialNumber().toString(16).toUpperCase(Locale.ROOT);
NotificationRequest request = new NotificationRequest.Builder().withSerialNumber(sn)
.withNonce(nonce)
.withTimestamp(timeStamp)
.withSignature(signature)
.withBody(obj)
.build();
NotificationHandler handler = new NotificationHandler(verifier, wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
// 验签和解析请求体
Notification notification = handler.parse(request);
JSONObject notifyResult = JSON.parseObject(notification.getDecryptData());
System.out.println("notifyResult:" + notifyResult);
//做一些操作
log.info(notifyResult.toString());
} catch (Exception e) {
log.error("微信支付回调失败", e);
//关闭订单
} finally {
//删除订单
// RedisUtils.deleteKeys(stuUserId);
}
}
/**
* 微信退款
*
* @param bo
* @return
* @throws Exception
*/
@PostMapping("/refund")
public Object refund(@RequestBody StuOrderBo bo) throws Exception {
JSONObject obj = new JSONObject();
obj.put("transaction_id", bo.getThirdOrderNo());
String outRefundNo = getOrderNumber();
obj.put("out_refund_no", outRefundNo); //退款的单号
JSONObject amount = new JSONObject();
//查询订单需要退款的金额
StuOrderVo stuOrderVo = iStuOrderService.getByOrderNo(bo.getOrderNo());
BigDecimal total = stuOrderVo.getOrderAmount();
amount.put("refund", total.intValue());
amount.put("total", total.intValue());
amount.put("currency", "CNY");
obj.put("amount", amount);
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setEntity(new StringEntity(obj.toJSONString(), "UTF-8"));
try {
//执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
String bodyAsString = EntityUtils.toString(response.getEntity());
JSONObject refundObject = JSONObject.parseObject(bodyAsString);
String refund_id = refundObject.getString("refund_id");
if (refund_id == null) {
String message = JSONObject.parseObject(bodyAsString).getString("message");
return message;
}
return "退款成功";
} catch (IOException e) {
//
}
}
/**
* 查询 微信支付订单
*
* @throws Exception
*/
@SaIgnore
@GetMapping("queryWxOrder/{openid}")
public R queryWxOrder(@PathVariable(value = "openid") @NotNull(message = "openid不可为空值") String openid) throws Exception {
StuOrderVo stuOrderVo = RedisUtils.getCacheObject(CacheConstants.STU_ORDER_KEY + openid);
if (ObjectUtils.isEmpty(stuOrderVo)) {
return R.fail("订单不存在");
}
//请求URL
URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + stuOrderVo.getOrderNo());
uriBuilder.setParameter("mchid", wxPayConfig.getMerchantId());
//完成签名并执行请求
HttpGet httpGet = new HttpGet(uriBuilder.build());
httpGet.addHeader("Accept", "application/json");
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
return R.ok("微信支付订单查询成功", JSONObject.parseObject(EntityUtils.toString(response.getEntity())));
} else if (statusCode == 204) {
return R.ok("成功", JSONObject.parseObject(EntityUtils.toString(response.getEntity())));
}
return R.ok(statusCode, "请求失败", JSONObject.parseObject(EntityUtils.toString(response.getEntity())));
} finally {
response.close();
}
}
/**
* 查询 微信支付订单
*
* @throws Exception
*/
@SaIgnore
@GetMapping("queryByOrderNo/{orderNo}")
public R queryByOrderNo(@PathVariable(value = "orderNo") @NotNull(message = "orderNo") String orderNo) throws Exception {
//请求URL
URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + orderNo);
uriBuilder.setParameter("mchid", wxPayConfig.getMerchantId());
//完成签名并执行请求
HttpGet httpGet = new HttpGet(uriBuilder.build());
httpGet.addHeader("Accept", "application/json");
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
return R.ok("微信支付订单查询成功", JSONObject.parseObject(EntityUtils.toString(response.getEntity())));
} else if (statusCode == 204) {
return R.ok("成功", JSONObject.parseObject(EntityUtils.toString(response.getEntity())));
}
return R.ok(statusCode, "请求失败", JSONObject.parseObject(EntityUtils.toString(response.getEntity())));
} finally {
response.close();
}
}
/**
* 关闭微信支付订单
*
* @throws Exception
*/
@SaIgnore
@PostMapping("/closeWxOrder/{openid}")
public R CloseWxOrder(@PathVariable(value = "openid") @NotNull(message = "openid 不可为空值") String openid) throws Exception {
StuOrderVo stuOrderVo = iStuOrderService.getByOpenidOrder(openid);
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + stuOrderVo.getOrderNo() + "/close");
//请求body参数
String reqdata = "{\"mchid\": \"" + wxPayConfig.getMerchantId() + "\"}";
StringEntity entity = new StringEntity(reqdata, "utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
RedisUtils.deleteKeys(CacheConstants.STU_ORDER_KEY + openid);
return R.ok("关闭成功", EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) {
RedisUtils.deleteKeys(CacheConstants.STU_ORDER_KEY + openid);
return R.ok("关闭成功", null);
}
return R.fail("请求失败", EntityUtils.toString(response.getEntity()));
} finally {
RedisUtils.deleteKeys(CacheConstants.STU_ORDER_KEY + openid);
response.close();
}
}
/**
* 获取openid
*
* @param
*/
@SaIgnore
@GetMapping(value = "/getOpenid")
public R login(String code) {
// 根据小程序穿过来的code想这个url发送请求
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + wxPayConfig.getAppId() + "&secret=" + wxPayConfig.getAppSecret() + "&js_code=" + code + "&grant_type=authorization_code";
// 发送请求,返回Json字符串
String str = WeChatUtil.httpRequest(url, "GET", null);
// 转成Json对象 获取openid
JSONObject jsonObject = JSONObject.parseObject(str);
// 我们需要的openid,在一个小程序中,openid是唯一的
String openid = jsonObject.get("openid").toString();
return R.ok("授权成功", openid);
}
private JSONObject sendHttpPost(JSONObject obj, String url) throws IOException {
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setEntity(new StringEntity(obj.toJSONString(), "UTF-8"));
try {
CloseableHttpResponse response = httpClient.execute(httpPost);
String bodyAsString = EntityUtils.toString(response.getEntity());
JSONObject jsonResult = JSONObject.parseObject(bodyAsString);
return jsonResult;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private JSONObject sendHttpGet(JSONObject obj, String url) throws IOException {
HttpGet httpGet = new HttpGet();
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setEntity(new StringEntity(obj.toJSONString(), "UTF-8"));
try {
CloseableHttpResponse response = httpClient.execute(httpPost);
String bodyAsString = EntityUtils.toString(response.getEntity());
JSONObject jsonResult = JSONObject.parseObject(bodyAsString);
return jsonResult;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 获取随机字符串 Nonce Str
*
* @return String 随机字符串
*/
public static String generateNonceStr() {
char[] nonceChars = new char[32];
for (int index = 0; index < nonceChars.length; ++index) {
nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
}
return new String(nonceChars);
}
/**
* 生产订单号
*
* @return
*/
private String getOrderNumber() {
return UUID.randomUUID().toString().replace("-", "");
}
到了这里,关于SpringBoot + 微信支付(小程序)案例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!