最近做微信支付,踩了一点坑。就留下代码防止各位入坑。哈不多说,上代码
service
package com.sjc.wx.service;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Map;
/**
* 微信下单功能接口
* @param
*/
@Service
public interface WxPayService {
Map<String,String> pay(String openId,String body, BigDecimal total_fee, String orderId, String shopId,String time_expire,String url);
Map<String, String> refunds(String orderSn, BigDecimal amount, BigDecimal orderAmount, String refundSn);
}
实现层
package com.sjc.wx.service.impl;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConfig;
import com.github.wxpay.sdk.WXPayRequest;
import com.github.wxpay.sdk.WXPayUtil;
import com.sjc.wx.service.WxPayService;
import com.sjc.wx.config.MyConfig;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;
@Service
public class WxPayServiceImp implements WxPayService {
@Resource
private WXPayConfig config;
@Override
/**
* body商品描述
* total_fee总金额 单位分
* orderId订单号
* shopId商品id
* body 商品信息
* time_expire 最晚支付时间
* url 回调地址
*/
public Map<String, String> pay(String openId,String body, BigDecimal total_fee, String orderId, String shopId,String time_expire,String url) {
Map resultMap=new HashMap();
MyConfig config = null;
WXPay wxpay =null;
//获取客户端的ip地址
//获取本机的ip地址
InetAddress addr = null;
try {
config = new MyConfig();
wxpay= new WXPay(config);
addr = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}
String spbill_create_ip = addr.getHostAddress();
//统一下单接口参数
HashMap<String, String> data = new HashMap<String, String>();
/*data.put("appid",config.getAppID());
data.put("mch_id",config.getMchID());
//生成的随机字符串
String nonce_str = WXPayUtil.generateNonceStr();
data.put("nonce_str", nonce_str);*/
data.put("body", body);//商品描述
data.put("out_trade_no",orderId); //商户订单号
//支付金额,需要转成字符串类型,否则后面的签名会失败
data.put("total_fee",(total_fee.multiply(new BigDecimal(100)).intValue()+""));
data.put("spbill_create_ip",spbill_create_ip);
data.put("notify_url",url);
data.put("trade_type","JSAPI");
data.put("product_id",shopId);
data.put("openid", openId);
if(null!=time_expire && !"".equals(time_expire)){
System.out.println("订单最晚支付时间:"+time_expire);
data.put("time_expire",time_expire);
}
try {
Map<String, String> rMap = wxpay.unifiedOrder(data);
System.out.println("统一下单接口返回: " + rMap);
String return_code = (String) rMap.get("return_code");
String result_code = (String) rMap.get("result_code");
String nonceStr = WXPayUtil.generateNonceStr();
if ("SUCCESS".equals(return_code) && return_code.equals(result_code)) {
String prepayid = rMap.get("prepay_id");
resultMap.put("appId", config.getAppID());//二次签名一定要这几个参数一个不能删否则会报签名验证失败
resultMap.put("package", "prepay_id="+prepayid);
resultMap.put("signType", "MD5");
//这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
resultMap.put("nonceStr",nonceStr);
//Long timeStamp = System.currentTimeMillis() / 1000;
Long timeStamp = System.currentTimeMillis() / 1000;
resultMap.put("timeStamp",timeStamp.toString());
//再次签名,这个签名用于小程序端调用wx.requesetPayment方法
String sign = WXPayUtil.generateSignature(resultMap, config.getKey());
resultMap.put("paySign", sign);
System.out.println("生成的签名paySign : "+ sign);
return resultMap;
}else{
return resultMap;
}
} catch (Exception e) {
e.printStackTrace();
return resultMap;
}
}
/**
* @param orderSn 支付单号
* @param amount 退款金额
* @param orderAmount 订单金额
* @param refundSn 退单号
* @return
*/
@Override
public Map<String, String> refunds(String orderSn, BigDecimal amount, BigDecimal orderAmount, String refundSn) {
Map<String, String> parameterMap = new TreeMap<>();
Map<String, String> map = new HashMap<>();
parameterMap.put("appid", config.getAppID());//公众账号ID
parameterMap.put("mch_id", config.getMchID());//商户号
parameterMap.put("nonce_str", DigestUtils.md5Hex(UUID.randomUUID() + RandomStringUtils.randomAlphabetic(30)));//随机字符串
parameterMap.put("out_refund_no", refundSn);//退单号
//parameterMap.put("out_refund_no", "YC0780012019071313132000001001");//退单号
parameterMap.put("out_trade_no", orderSn);//支付单号
//parameterMap.put("out_trade_no", "YC0780012019071313132000001");//支付单号
parameterMap.put("total_fee", orderAmount.intValue() + "");//订单金额
parameterMap.put("refund_fee", amount.intValue() + "");//退款金额
//parameterMap.put("total_fee", "1");//订单金额
//parameterMap.put("refund_fee", "1");//退款金额
String sign = null;
Map<String, String> resultMap;
try {
sign = WXPayUtil.generateSignature(parameterMap, config.getKey());
parameterMap.put("sign", sign);
String xml = WXPayUtil.mapToXml(parameterMap);
WXPayRequest wxPayRequest = new WXPayRequest(config);
String result = wxPayRequest.requestOnce("api.mch.weixin.qq.com","/secapi/pay/refund","",xml,config.getHttpConnectTimeoutMs(),config.getHttpReadTimeoutMs(),true);
resultMap = WXPayUtil.xmlToMap(result) ;
String return_code = resultMap.get("return_code");
String return_msg = resultMap.get("return_msg");
String result_code = resultMap.get("result_code");
if ("SUCCESS".equals(return_code) && result_code != null && "SUCCESS".equals(result_code)) {
map.put("return_code", return_code);
map.put("return_msg", return_msg);
map.put("result_code", result_code);
map.put("out_refund_no", resultMap.get("out_refund_no"));
map.put("refund_fee", resultMap.get("refund_fee"));
} else {
map.put("return_code", return_code);
map.put("return_msg", return_msg);
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
}
支付调用
/**'
* 缴纳押金支付
* @param
* @param total_fee
* @param userName
* @return
*/
@ResponseBody
@RequestMapping(value = "/payWxDeposit", method = RequestMethod.POST)
public R payWxDeposit(String openId, BigDecimal total_fee, String userName ) {
GenerateBill generateBill = new GenerateBill();
BaseResponse<Map> response = null;
if (null == total_fee) {
return R.error("支付失败,参数不正确");
}
String orderId = generateBill.generateContractNo("YJ", (((int) Math.random()) * 1000));
Map<String, String> map = wxPayService.pay(openId, "缴纳押金", total_fee, orderId, "YJ", "","https://你的ip/wx/pay/depositCallback");
map.put("orderId", orderId);
redisTemplate.opsForValue().set(orderId,userName,3600,TimeUnit.SECONDS);
System.out.println(redisTemplate.opsForValue().get(orderId));
return R.ok("调用支付缴纳押金成功", map);
}
@ResponseBody
@RequestMapping(value = "/payCoupon", method = RequestMethod.POST)
public R payCoupon(String openId, BigDecimal total_fee, String userName,CouponDO couponDO) {
GenerateBill generateBill = new GenerateBill();
BaseResponse<Map> response = null;
if (null == total_fee) {
return R.error("支付失败,参数不正确");
}
String orderId = generateBill.generateContractNo("coupon", ((int) Math.random() * 1000));
Map<String, String> map = wxPayService.pay(openId, "购买时间券", total_fee, orderId, "coupon", "","https://你的回调ip/你的回调方法/couponCallback");
map.put("orderId", orderId);
redisTemplate.opsForValue().set(orderId,couponDO,3600,TimeUnit.SECONDS);
System.out.println(redisTemplate.opsForValue().get(orderId));
return R.ok("调用购买时间券支付接口成功", map);
}
回调
@RequestMapping("depositCallback")
public String depositCallback(HttpServletRequest request, HttpServletResponse response) {
//System.out.println("微信支付成功,微信发送的callback信息,请注意修改订单信息");
InputStream is = null;
try {
try {
is = request.getInputStream();//获取请求的流信息(这里是微信发的xml格式所有只能使用流来读)
} catch (IOException e) {
e.printStackTrace();
}
StringBuilder sb = new StringBuilder();
String line = "";
BufferedReader br = new BufferedReader(new InputStreamReader(is));
while (true) {
try {
if (!((line = br.readLine()) != null)) break;
} catch (IOException e) {
e.printStackTrace();
}
sb.append(line);
}
String xml = sb.toString();
Map<String, String> notifyMap = null;//将微信发的xml转map
try {
notifyMap = WXPayUtil.xmlToMap(xml);
} catch (Exception e) {
e.printStackTrace();
}
if (notifyMap.get("return_code").equals("SUCCESS")) {
if (notifyMap.get("result_code").equals("SUCCESS")) {
String ordersSn = notifyMap.get("out_trade_no");//商户订单号
String amountpaid = notifyMap.get("total_fee");//实际支付的订单金额:单位 分
BigDecimal amountPay = (new BigDecimal(amountpaid).divide(new BigDecimal("100"))).setScale(2);//将分转换成元-实际支付金额:元
//String openid = notifyMap.get("openid"); //如果有需要可以获取
//String trade_type = notifyMap.get("trade_type");
/*以下是自己的业务处理------仅做参考
* 更新order对应字段/已支付金额/状态码
*/
Map map = new HashMap();
map.put("orderId", ordersSn);
List<DepositDO> list = depositService.list(map);
System.out.println(" wxPayController depositCallback 方法 313行代码 进入成功");
if (list.size() > 0) {
System.out.println(" wxPayController depositCallback 方法 313行代码 进入if成功");
//告诉微信服务器收到信息了,不要在调用回调action了========这里很重要回复微信服务器信息用流发送一个xml即可
response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
is.close();
} else {
System.out.println(" wxPayController depositCallback 方法 320行代码 进入else成功");
String userName = redisTemplate.opsForValue().get(ordersSn).toString();
map.put("userName",userName);
List<MemberDO> memberDO = memberService.list(map);
DepositDO depositDO = new DepositDO();
depositDO.setUserId(memberDO.get(0).getUserId());
depositDO.setMoney(depositConfigService.list(new HashMap<>()).get(0).getMoney());
depositDO.setTotalMoney(memberDO.get(0).getDepositNum().add(depositDO.getMoney()));
depositDO.setOrderId(ordersSn);
depositDO.setMoneyState(0);
depositService.save(depositDO);
//告诉微信服务器收到信息了,不要在调用回调action了========这里很重要回复微信服务器信息用流发送一个xml即可
response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
is.close();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@RequestMapping("couponCallback")
public String couponCallback(HttpServletRequest request, HttpServletResponse response) {
//System.out.println("微信支付成功,微信发送的callback信息,请注意修改订单信息");
InputStream is = null;
try {
try {
is = request.getInputStream();//获取请求的流信息(这里是微信发的xml格式所有只能使用流来读)
} catch (IOException e) {
e.printStackTrace();
}
StringBuilder sb = new StringBuilder();
String line = "";
BufferedReader br = new BufferedReader(new InputStreamReader(is));
while (true) {
try {
if (!((line = br.readLine()) != null)) break;
} catch (IOException e) {
e.printStackTrace();
}
sb.append(line);
}
String xml = sb.toString();
Map<String, String> notifyMap = null;//将微信发的xml转map
try {
notifyMap = WXPayUtil.xmlToMap(xml);
} catch (Exception e) {
e.printStackTrace();
}
if (notifyMap.get("return_code").equals("SUCCESS")) {
if (notifyMap.get("result_code").equals("SUCCESS")) {
String ordersSn = notifyMap.get("out_trade_no");//商户订单号
String amountpaid = notifyMap.get("total_fee");//实际支付的订单金额:单位 分
BigDecimal amountPay = (new BigDecimal(amountpaid).divide(new BigDecimal("100"))).setScale(2);//将分转换成元-实际支付金额:元
//String openid = notifyMap.get("openid"); //如果有需要可以获取
//String trade_type = notifyMap.get("trade_type");
/*以下是自己的业务处理------仅做参考
* 更新order对应字段/已支付金额/状态码
*/
Map map = new HashMap();
map.put("orderId", ordersSn);
List<CouponDO> couponDOList = couponService.list(map);
System.out.println(" wxPayController couponCallback 方法 388行代码 进入成功");
if (couponDOList.size() > 0) {
//告诉微信服务器收到信息了,不要在调用回调action了========这里很重要回复微信服务器信息用流发送一个xml即可
response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
is.close();
} else {
System.out.println(" wxPayController couponCallback 方法 394行代码 进入else成功");
CouponDO coupon = (CouponDO) redisTemplate.opsForValue().get(ordersSn);
Date date = new Date();
MemberDO memberDO = memberService.get(coupon.getUserId());
int Totalcount = memberDO.getTimeCoupon();
Totalcount += coupon.getCount();
coupon.setTotalCount(Totalcount);
coupon.setTime(date);
coupon.setState(0);
coupon.setIntype(0);
coupon.setInstr("+");
coupon.setDesc("充值");
if(couponService.save(coupon)>0){
memberDO.setTimeCoupon(Totalcount);
memberService.update(memberDO);
}
//告诉微信服务器收到信息了,不要在调用回调action了========这里很重要回复微信服务器信息用流发送一个xml即可
response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
is.close();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
退款
Map<String,String> resultMap = wxPayService.refunds(data.getOrderId(),data.getMoney().multiply(new BigDecimal(""+100)),data.getMoney().multiply(new BigDecimal(""+100))
,data.getOrderId()+"OUT");
String return_code = resultMap.get("return_code");
String return_msg = resultMap.get("return_msg");
String result_code = resultMap.get("result_code");
if ("SUCCESS".equals(return_code) && result_code != null && "SUCCESS".equals(result_code)) {
DepositDO depositDO = new DepositDO();
depositDO.setId(data.getId());
depositDO.setUpdateDate(date);
depositDO.setTotalMoney(count);
depositDO.setState(1);//设置状态为体现
depositDO.setMoneyState(1);//设置状态为体现
depositService.update(depositDO);
memberService.update(memberDO);
sp.setTotalMoney(count);//更新押金表总金额
spService.update(sp);//退押金审批表数据更新
return R.ok("退款成功");
}else{
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return R.error("微信退款失败");
}
退款方法可直接用
/**
* 支付交易订单号,amount退款金额,orderAmount订单金额,refundSn退货单号
* @return
* @throws Exception
*/
/*@ResponseBody
@PostMapping("refunds")
public Map<String, Object> refunds(String orderSn, BigDecimal amount, BigDecimal orderAmount, String refundSn)
throws Exception {
Map<String, String> parameterMap = new TreeMap<>();
parameterMap.put("appid", config.getAppID());//公众账号ID
parameterMap.put("mch_id", config.getMchID());//商户号
parameterMap.put("nonce_str", DigestUtils.md5Hex(UUID.randomUUID() + RandomStringUtils.randomAlphabetic(30)));//随机字符串
parameterMap.put("out_refund_no", refundSn);//退单号
//parameterMap.put("out_refund_no", "YC0780012019071313132000001001");//退单号
parameterMap.put("out_trade_no", orderSn);//支付单号
//parameterMap.put("out_trade_no", "YC0780012019071313132000001");//支付单号
parameterMap.put("total_fee", orderAmount.intValue() + "");//订单金额
parameterMap.put("refund_fee", amount.intValue() + "");//退款金额
//parameterMap.put("total_fee", "1");//订单金额
//parameterMap.put("refund_fee", "1");//退款金额
String sign = WXPayUtil.generateSignature(parameterMap, config.getKey());
parameterMap.put("sign", sign);
String xml = WXPayUtil.mapToXml(parameterMap);
WXPayRequest wxPayRequest = new WXPayRequest(config);
String result = wxPayRequest.requestOnce("api.mch.weixin.qq.com","/secapi/pay/refund","",xml,config.getHttpConnectTimeoutMs(),config.getHttpReadTimeoutMs(),true);
Map<String, String> resultMap = WXPayUtil.xmlToMap(result) ;
String return_code = resultMap.get("return_code");
String return_msg = resultMap.get("return_msg");
String result_code = resultMap.get("result_code");
Map<String, Object> map = new HashMap<>();
if ("SUCCESS".equals(return_code) && result_code != null && "SUCCESS".equals(result_code)) {
map.put("return_code", return_code);
map.put("return_msg", return_msg);
map.put("result_code", result_code);
map.put("out_refund_no", resultMap.get("out_refund_no"));
map.put("refund_fee", resultMap.get("refund_fee"));
} else {
map.put("return_code", return_code);
map.put("return_msg", return_msg);
}
return map;
}*/
我继承了一个抽象方法 这个是微信配置
package com.sjc.wx.config;
import com.github.wxpay.sdk.IWXPayDomain;
import com.github.wxpay.sdk.WXPayConfig;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
/**
* 微信支付配置类填写重要信息
*/
@Component
public class MyConfig extends WXPayConfig {
private byte[] certData;
//private InputStream inputStream;
public MyConfig(){
// String certPath = "classpath:/resources/apiclient_cert.p12";
// String certPath = Objects.requireNonNull(Token.class.getClassLoader().getResource("apiclient_cert.p12")).getPath();
// String property = System.getProperties().getProperty("os.name");
// if (!property.startsWith("W")) {
// certPath = "/usr/local/app/apiclient_cert.p12";
// }
// File file = new File(certPath);
// // Resource resource = new FileSystemResourceLoader().getResource("classpath:apiclient_cert.p12");
// //String path = resource.getURL().getPath();
// InputStream certStream = new FileInputStream(file);
// this.certData = new byte[(int) file.length()];
// certStream.read(this.certData);
// certStream.close();
}
@Override
public String getAppID() {
return "你的微信商户appid需要填写";
}
@Override
public String getMchID() {
return "你的微信支付商户id需要填写";
}
@Override
public String getKey() {
return "apikey需要的";
}
public String getOpenId() {
return "";//这个不需要可以弄做测试
}
/**
这个是放cert安全证书的
*/
@Override
public InputStream getCertStream() {
org.springframework.core.io.ClassPathResource classPathResource = new ClassPathResource("apiclient_cert.p12");
// String certPath = classPathResource.getPath();
try {
return classPathResource.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
public int getHttpConnectTimeoutMs() {
return 8000;
}
@Override
public int getHttpReadTimeoutMs() {
return 10000;
}
@Override
public IWXPayDomain getWXPayDomain() {
return new IWXPayDomain() {
@Override
public void report(String domain, long elapsedTimeMillis, Exception ex) {
}
@Override
public DomainInfo getDomain(WXPayConfig config) {
return new DomainInfo("api.mch.weixin.qq.com", false);
}
};
}
public static void main(String[] args) {
String path = MyConfig.class.getProtectionDomain().getCodeSource().getLocation().getPath();
System.out.println(path);
}
}
就到此位置了,我没有做退款回调,如果要做退款加库存的可以做按照以上操作。
如果看到R.OK不知道是啥这里我就简述一下,自己的框架封装我给附上代码,做这个踩了不少坑以上代码参数不要删除,像微信统一下单 第二次签名那别动,动了一直显示签名不对 我之前是没给appid做签名,扫码的时候一直显示这个。。唉。
package com.sjc.common.utils;
import io.swagger.annotations.ApiModel;
import java.util.HashMap;
import java.util.Map;
@ApiModel("公共的返回类")
public class R extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
public R() {
put("code", 200);
put("status", "success");
put("msg", "操作成功");
}
public static R error() {
return error(1, "操作失败");
}
public static R error(String msg) {
return error(500, msg);
}
public static R error(int code, String msg) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static R ok(String msg) {
R r = new R();
r.put("code", 200);
r.put("msg", msg);
return r;
}
public static R ok(int code,String msg,Map map) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
r.put("data", map);
return r;
}
public static R ok(String msg,Map map) {
R r = new R();
r.put("code", 200);
r.put("msg", msg);
r.put("status", "success");
r.put("data", map);
return r;
}
public static R ok(Map<String, Object> map) {
R r = new R();
r.putAll(map);
return r;
}
public static R ok() {
return new R();
}
@Override
public R put(String key, Object value) {
super.put(key, value);
return this;
}
}
觉得我对您有帮助的话就打赏一下哦,
具体不懂的话 看下这个博客吧 https://blog.youkuaiyun.com/hexiaohua95/article/details/85016270