parent
db4982112b
commit
ccf71b60a7
12 changed files with 620 additions and 19 deletions
@ -0,0 +1,43 @@ |
|||||||
|
package org.springblade.weixin.controller; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import org.springblade.core.mp.support.Condition; |
||||||
|
import org.springblade.core.mp.support.Query; |
||||||
|
import org.springblade.core.tool.api.R; |
||||||
|
import org.springblade.core.tool.utils.Func; |
||||||
|
import org.springblade.weixin.entity.WeChatAddress; |
||||||
|
import org.springblade.weixin.entity.WeChatUser; |
||||||
|
import org.springblade.weixin.service.IWeChatAddressService; |
||||||
|
import org.springframework.web.bind.annotation.*; |
||||||
|
|
||||||
|
@RestController |
||||||
|
@AllArgsConstructor |
||||||
|
@RequestMapping("/address") |
||||||
|
public class WeChatAddressController { |
||||||
|
|
||||||
|
private final IWeChatAddressService weChatAddressService; |
||||||
|
|
||||||
|
@GetMapping("/list") |
||||||
|
public R<IPage<WeChatAddress>> list(WeChatUser weChatUser, Query query) { |
||||||
|
// return R.data(weChatAddressService.getPage(weChatUser, Condition.getPage(query)));
|
||||||
|
return R.data(weChatAddressService.page(Condition.getPage(query), Wrappers.<WeChatAddress>lambdaQuery().eq(weChatUser.getId() != null, WeChatAddress::getUserId, weChatUser.getId()))); |
||||||
|
} |
||||||
|
|
||||||
|
@PostMapping("/save") |
||||||
|
public R save(@RequestBody WeChatAddress weChatAddress) { |
||||||
|
return R.status(weChatAddressService.add(weChatAddress)); |
||||||
|
} |
||||||
|
|
||||||
|
@PostMapping("/update") |
||||||
|
public R update(@RequestBody WeChatAddress weChatAddress) { |
||||||
|
return R.status(weChatAddressService.edit(weChatAddress)); |
||||||
|
} |
||||||
|
|
||||||
|
@PostMapping("/delete") |
||||||
|
public R delete(@RequestParam String ids) { |
||||||
|
return R.status(weChatAddressService.deleteLogic(Func.toLongList(ids))); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,42 @@ |
|||||||
|
package org.springblade.weixin.entity; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||||
|
import lombok.Data; |
||||||
|
import org.springblade.core.mp.base.BaseEntity; |
||||||
|
|
||||||
|
@Data |
||||||
|
@TableName("eh_address") |
||||||
|
public class WeChatAddress extends BaseEntity { |
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
/** |
||||||
|
* 用户id |
||||||
|
*/ |
||||||
|
private Long userId; |
||||||
|
|
||||||
|
/** |
||||||
|
* 收货人 |
||||||
|
*/ |
||||||
|
private String consignee; |
||||||
|
|
||||||
|
/** |
||||||
|
* 手机号码 |
||||||
|
*/ |
||||||
|
private String phone; |
||||||
|
|
||||||
|
/** |
||||||
|
* 所在地区 |
||||||
|
*/ |
||||||
|
private String area; |
||||||
|
|
||||||
|
/** |
||||||
|
* 详细地址 |
||||||
|
*/ |
||||||
|
private String address; |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否默认地址(0否 1是) |
||||||
|
*/ |
||||||
|
private Integer isDefault; |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* |
||||||
|
* Redistributions of source code must retain the above copyright notice, |
||||||
|
* this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* Neither the name of the dreamlu.net developer nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* Author: Chill 庄骞 (smallchill@163.com) |
||||||
|
*/ |
||||||
|
package org.springblade.weixin.mapper; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||||
|
import org.springblade.weixin.entity.WeChatAddress; |
||||||
|
import org.springblade.weixin.entity.WeChatUser; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* Mapper 接口 |
||||||
|
* @author BladeX |
||||||
|
*/ |
||||||
|
public interface WeChatAddressMapper extends BaseMapper<WeChatAddress> { |
||||||
|
|
||||||
|
List<WeChatAddress> getPage(IPage<WeChatAddress> page, WeChatUser weChatUser); |
||||||
|
} |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||||
|
<mapper namespace="org.springblade.weixin.mapper.WeChatAddressMapper"> |
||||||
|
|
||||||
|
<select id="getPage" resultType="org.springblade.weixin.entity.WeChatAddress"> |
||||||
|
SELECT |
||||||
|
ad.id, |
||||||
|
ad.consignee, |
||||||
|
ad.phone, |
||||||
|
ad.area, |
||||||
|
ad.address, |
||||||
|
ad.is_default, |
||||||
|
ad.status |
||||||
|
FROM eh_wx_user AS us |
||||||
|
LEFT JOIN eh_address AS ad ON us.id = ad.user_id |
||||||
|
WHERE |
||||||
|
us.is_deleted = 0 AND ad.is_deleted = 0 |
||||||
|
<if test="weChatUser.openId != null and weChatUser.openId != ''"> |
||||||
|
AND us.open_id = #{weChatUser.openId} |
||||||
|
</if> |
||||||
|
ORDER BY ad.create_time DESC |
||||||
|
</select> |
||||||
|
</mapper> |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* |
||||||
|
* Redistributions of source code must retain the above copyright notice, |
||||||
|
* this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* Neither the name of the dreamlu.net developer nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* Author: Chill 庄骞 (smallchill@163.com) |
||||||
|
*/ |
||||||
|
package org.springblade.weixin.service; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||||
|
import org.springblade.core.mp.base.BaseService; |
||||||
|
import org.springblade.weixin.entity.WeChatAddress; |
||||||
|
import org.springblade.weixin.entity.WeChatUser; |
||||||
|
|
||||||
|
/** |
||||||
|
* 服务类 |
||||||
|
* |
||||||
|
* @author BladeX |
||||||
|
*/ |
||||||
|
public interface IWeChatAddressService extends BaseService<WeChatAddress> { |
||||||
|
|
||||||
|
IPage<WeChatAddress> getPage(WeChatUser weChatUser, IPage<WeChatAddress> page); |
||||||
|
|
||||||
|
boolean add(WeChatAddress weChatAddress); |
||||||
|
|
||||||
|
boolean edit(WeChatAddress weChatAddress); |
||||||
|
} |
||||||
@ -0,0 +1,71 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2018-2028, Chill Zhuang All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* |
||||||
|
* Redistributions of source code must retain the above copyright notice, |
||||||
|
* this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* Neither the name of the dreamlu.net developer nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* Author: Chill 庄骞 (smallchill@163.com) |
||||||
|
*/ |
||||||
|
package org.springblade.weixin.service.impl; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import org.springblade.core.mp.base.BaseServiceImpl; |
||||||
|
import org.springblade.weixin.entity.WeChatAddress; |
||||||
|
import org.springblade.weixin.entity.WeChatUser; |
||||||
|
import org.springblade.weixin.mapper.WeChatAddressMapper; |
||||||
|
import org.springblade.weixin.service.IWeChatAddressService; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
import org.springframework.transaction.annotation.Transactional; |
||||||
|
|
||||||
|
/** |
||||||
|
* 服务实现类 |
||||||
|
*/ |
||||||
|
@Service |
||||||
|
@AllArgsConstructor |
||||||
|
public class WeChatAddressServiceImpl extends BaseServiceImpl<WeChatAddressMapper, WeChatAddress> implements IWeChatAddressService { |
||||||
|
|
||||||
|
private final WeChatAddressMapper weChatAddressMapper; |
||||||
|
|
||||||
|
@Override |
||||||
|
public IPage<WeChatAddress> getPage(WeChatUser weChatUser, IPage<WeChatAddress> page) { |
||||||
|
return page.setRecords(weChatAddressMapper.getPage(page, weChatUser)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
@Transactional(rollbackFor = Exception.class) |
||||||
|
public boolean add(WeChatAddress weChatAddress) { |
||||||
|
weChatAddress.setIsDefault(0); |
||||||
|
// if (weChatAddress.getIsDefault() == 1) {
|
||||||
|
// WeChatAddress address = this.getOne(Wrappers.<WeChatAddress>lambdaQuery().eq(WeChatAddress::getUserId, weChatAddress.getUserId()).eq(WeChatAddress::getIsDefault, 1));
|
||||||
|
// if (address != null) {
|
||||||
|
// address.setIsDefault(0);
|
||||||
|
// this.updateById(address);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
return this.save(weChatAddress); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
@Transactional(rollbackFor = Exception.class) |
||||||
|
public boolean edit(WeChatAddress weChatAddress) { |
||||||
|
if (weChatAddress.getIsDefault() == 1) { |
||||||
|
WeChatAddress address = this.getOne(Wrappers.<WeChatAddress>lambdaQuery().eq(WeChatAddress::getUserId, weChatAddress.getUserId()).eq(WeChatAddress::getIsDefault, 1)); |
||||||
|
if (address != null && !address.getId().equals(weChatAddress.getId())) { |
||||||
|
address.setIsDefault(0); |
||||||
|
this.updateById(address); |
||||||
|
} |
||||||
|
} |
||||||
|
return this.updateById(weChatAddress); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,329 @@ |
|||||||
|
package org.springblade.weixin.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 = "wx432c2efe6df3b97a"; |
||||||
|
|
||||||
|
//微信支付商户号 微信支付-账户中心-商户信息-微信支付商户号.查看
|
||||||
|
public static String mchId = "1650865884"; |
||||||
|
|
||||||
|
// 微信下单回调地址,支付成功后会回调这个地址。
|
||||||
|
//对应文档 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 = "859df8b167e74223e9237dee1b344524"; |
||||||
|
|
||||||
|
// 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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue