|
|
@@ -1,31 +1,29 @@
|
|
|
package cn.iocoder.yudao.module.system.controller.admin.pay;
|
|
|
|
|
|
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
|
|
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
|
|
+import cn.iocoder.yudao.module.system.controller.admin.pay.vo.WechatUnifiedOrderRequest;
|
|
|
import cn.iocoder.yudao.module.system.util.pay.CertUtils;
|
|
|
-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.exception.HttpCodeException;
|
|
|
-import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
|
|
|
-import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
|
|
|
-import com.wechat.pay.contrib.apache.httpclient.util.RsaCryptoUtil;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.wechat.pay.java.core.Config;
|
|
|
+import com.wechat.pay.java.core.RSAPublicKeyConfig;
|
|
|
+import com.wechat.pay.java.service.partnerpayments.jsapi.JsapiServiceExtension;
|
|
|
+import com.wechat.pay.java.service.partnerpayments.jsapi.model.Amount;
|
|
|
+import com.wechat.pay.java.service.partnerpayments.jsapi.model.Payer;
|
|
|
+import com.wechat.pay.java.service.partnerpayments.jsapi.model.PrepayRequest;
|
|
|
+import com.wechat.pay.java.service.partnerpayments.jsapi.model.PrepayWithRequestPaymentResponse;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
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.aspectj.lang.annotation.Before;
|
|
|
+import org.apache.http.util.EntityUtils;
|
|
|
+import org.springframework.http.MediaType;
|
|
|
+import org.springframework.web.bind.annotation.PostMapping;
|
|
|
import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
import org.springframework.web.bind.annotation.RestController;
|
|
|
|
|
|
-import javax.crypto.BadPaddingException;
|
|
|
-import javax.crypto.IllegalBlockSizeException;
|
|
|
-import java.io.IOException;
|
|
|
-import java.nio.charset.StandardCharsets;
|
|
|
-import java.security.GeneralSecurityException;
|
|
|
-import java.security.PrivateKey;
|
|
|
+import javax.annotation.security.PermitAll;
|
|
|
+import java.util.Arrays;
|
|
|
|
|
|
/**
|
|
|
* 微信小程序统一下单控制器
|
|
|
@@ -35,10 +33,6 @@ import java.security.PrivateKey;
|
|
|
@RequestMapping("/pay/wx")
|
|
|
public class WeChatPayController {
|
|
|
|
|
|
- private CloseableHttpClient httpClient;
|
|
|
- private CertificatesManager certificatesManager;
|
|
|
- private Verifier verifier;
|
|
|
-
|
|
|
private static final String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzbDDaK6pFz8fEBIM/pFlIV4uApuhr/TDm8mlK0+Cw1+om1p2s2RFItgBw7Gk9qTN72L68utj5bEDJRQgdr1ixAjc4AH1AOYeV4YZnWTQkcjsRiEimqo+bkIm+osTpFEO9kdvfFMILOl1MDipIAcUrNq9CMs6s6nFDLxLlwy8Ap4VrP4N+FF04efG+8dj35z8dUdi/8WrAe4p7GYGojHHoZ/mIqEDnLdt+PM5pRizCfUCtmSF5UxnmNUlUAutIedvtOAT9rs9E71aO+ldhXNMfA+jLxuBmyp+oy3hWS4Vqp4RbC1a2xRSULvEgSUwrdNq/rytpK66/XY7Vk2f/onCdAgMBAAECggEAXtI/ox1OeGSN01b7MggeMzApBo2u6Vs+m97irGv7JBqHoYzseWuiScX9x6/5EmA4WCw86Srp/i8qsfOsjVucyLc+DXecufNtZ1VvnXC0MMq50tMuaf5btMAXO8tzuyY9gpLyGxwyTJLEuawGxlVBKUxWJOsK9LFVuEbJeunDDlmWsn+euRw3F7jxtNnjVjfqXsY9O+TtB8p0bOduWZc7OvDHoIZQL4NnDivnAR5q1dSLt77pzdinvGiXNYNuTTULstR+KVhJy5Kmsldtj3nrP/fcy/N+JsXm9+3xgaJ2edKwmfnwvQjJAWOtltOSoHx4RK3kzbC8UhKs/E3iaUEUgQKBgQDp0qoNKHbTCiwDhmrnnCyaoW16qEOj0MqHOy5mKYmJkcSDGgNQM/44gN7vOu5BBJHXdkizxaoX3w/ufW2ahhJ/LcjITjFIq8gpiFKbLNfhkCw47ZDtmhB9jAnaB+9+rLeAFXkfXKi+PYJGZIIzbgn0u5LADzRjvzaxe/Z+Ed5ooQKBgQDEcKkk0SmcsHdTkLs2kf6fu8z7MAg9l42fCO18TPt3PeoRcvf8AKw+WDjARSo0ojqL23T9U0IydKdsbcxrYsHETUBS+MAP09Ckpd8p7F5evJghXwhhQRx0gzVuNMkbeP1a3xnC3yL7V5fluGQqz7Xiv3sg9dxYDh8cVQBu6ckafQKBgQCcoF0Ay2YtH9cz1UqvMtI+Enw/eY81oJrJ0z7VeGWFHXvBRh+KDgnw14J+Rb9rFiCLb9Rrd7DkpKsLWkGdDMo/HvAsHRSuVUOTbpnHEFbb5bN5vskiH92D+9Ztkns/I3sX9UpZU7xFEva9KH5+7OsGYM+Aj67MUj3UzfDjqhyNgQKBgFcNojeZpbo1jbvvsLd/PXqmLDHI2G4LIoyu1Se3qdzvCDLRY0o/NhWu3P9/5zNKDW37RD4bToOzpJptkiCotDv9DBt49wxMjvLYOyyF/lA3faeUSM9onmaX2u7K37CYDpbdtbnhTsxZxgvcii9auz0QJE24BvzSzUCt/rIoUqG1AoGAX9sTEp77kdVrM8ECEKgLsOtRWEAgeNkWLgHZKYcEYyu3D4XEVScYNtTBLBHXs3n1GIYdLjWEAWWb55sqKSlVzVWfOhpnOqoFq51xi2TGr3pkp9cqZf0lxYKs7P4tlgeL0icXfb+lFTPmgfgE5vjHBvYQcj7o9lO5hRe46fVoxT8=";
|
|
|
|
|
|
private static final String merchantId = "1105835366";
|
|
|
@@ -68,82 +62,70 @@ public class WeChatPayController {
|
|
|
"rXbHeFgiQcZb/sR8sTBV0KXR";
|
|
|
|
|
|
private static final String apiV3Key = "W2sE4rF6tY8uI0oP1aQ2wS3dF4gH5jK6";
|
|
|
- public void setup() throws Exception {
|
|
|
+
|
|
|
+ private static final String wechatpayPublicKeyPem = "-----BEGIN PUBLIC KEY-----\n" +
|
|
|
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuh82JjfQ/r0+mz8W9ZAr\n" +
|
|
|
+ "Uw5nLSSzUKxvepSnWny0a7ccZFB+j8q9pZWq7gmfyOmsC6uq8gmzPDnOD+xrBfj7\n" +
|
|
|
+ "t8R3x/VU7tpmGtdNecQFTaJ6NrausM82sMVdf9XOpQvERgVtfIO5U1q3jqSfTyNk\n" +
|
|
|
+ "MchXhQMoWa3RXDl7EO0okLIKoNgA1o4AWZZaVyiJ0BjF/URwk6O0eg+g2PqDldFr\n" +
|
|
|
+ "RxQIHLsaFnFsDJx5bNia9Jb48g+nNpoKtW1njsPDn/ckTOvnZxdkbkwIaQ2NDli1\n" +
|
|
|
+ "ehsaDK6ACReD/A6nIlDQB50duKhVNzx10MZwikD2OWAPUnY6rIE9cqWwKUH4okWJ\n" +
|
|
|
+ "BwIDAQAB\n" +
|
|
|
+ "-----END PUBLIC KEY-----\n";
|
|
|
+
|
|
|
+ //Call Unified Order API
|
|
|
+ @PostMapping(value = "/unifiedOrder", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE, MediaType.APPLICATION_JSON_VALUE})
|
|
|
+ @PermitAll
|
|
|
+ @TenantIgnore
|
|
|
+ public CommonResult<String> unifiedOrder() throws Exception {
|
|
|
String merchantSerialNumber = CertUtils.getMerchantSerialNumberFromPem(certPem);
|
|
|
- PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey);
|
|
|
- certificatesManager = CertificatesManager.getInstance();
|
|
|
- certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId,
|
|
|
- new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),
|
|
|
- apiV3Key.getBytes(StandardCharsets.UTF_8));
|
|
|
- verifier = certificatesManager.getVerifier(merchantId);
|
|
|
- WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
|
|
|
- .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
|
|
|
- .withValidator(new WechatPay2Validator(verifier));
|
|
|
- httpClient = builder.build();
|
|
|
- }
|
|
|
+ // 1. 构建配置类(就是你之前写的那部分)
|
|
|
+ Config config =
|
|
|
+ new RSAPublicKeyConfig.Builder()
|
|
|
+ .merchantId(merchantId)
|
|
|
+ .privateKey(privateKey) // 商户私钥字符串
|
|
|
+ .publicKey(wechatpayPublicKeyPem) // 微信支付公钥字符串
|
|
|
+ .publicKeyId("PUB_KEY_ID_0111058353662026030200381966000601") // 公钥ID(必须!)
|
|
|
+ .merchantSerialNumber(merchantSerialNumber)
|
|
|
+ .apiV3Key(apiV3Key)
|
|
|
+ .build();
|
|
|
|
|
|
- public void encryptedTest() throws IllegalBlockSizeException {
|
|
|
- String text = "helloWorld";
|
|
|
- String transformation = "RSA/ECB/PKCS1Padding";
|
|
|
- String encryptedText = RsaCryptoUtil.encrypt(text, verifier.getValidCertificate(), transformation);
|
|
|
- System.out.println(encryptedText);
|
|
|
- }
|
|
|
+ // 2. 创建 JSAPI 扩展服务
|
|
|
+ JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
|
|
|
|
|
|
- public void decryptedTest() throws BadPaddingException {
|
|
|
- String encryptedText = "";
|
|
|
- String transformation = "RSA/ECB/PKCS1Padding";
|
|
|
- String decryptedText = RsaCryptoUtil.decrypt(encryptedText, PemUtil.loadPrivateKey(privateKey), transformation);
|
|
|
- assert("helloWorld".equals(decryptedText));
|
|
|
- }
|
|
|
+ // 3. 构造统一下单请求
|
|
|
+ PrepayRequest request = new PrepayRequest();
|
|
|
+ Amount amount = new Amount();
|
|
|
+ amount.setTotal(1); // 金额,单位:分
|
|
|
+ request.setAmount(amount);
|
|
|
+ request.setSpAppid("wx0fe6b44ff9e5d4d6");
|
|
|
+ request.setSpMchid(merchantId);
|
|
|
+ request.setSubAppid("wx0fe6b44ff9e5d4d6");
|
|
|
+ request.setSpMchid("1739815694");
|
|
|
+ request.setDescription("测试商品");
|
|
|
+ request.setNotifyUrl("https://47.112.126.153:48080//pay/wx/callback");
|
|
|
+ request.setOutTradeNo("OUT_TRADE_NO_20250303_001");
|
|
|
|
|
|
+ Payer payer = new Payer();
|
|
|
+ payer.setSpOpenid("omRZq10rC8JR5SysFqVj82CJDqVk"); // 必须传入用户的openid
|
|
|
+ request.setPayer(payer);
|
|
|
+ String requestPaymentAppid = request.getSubAppid() != null ? request.getSubAppid() : request.getSpAppid();
|
|
|
+ // 4. 调用扩展方法,直接得到前端调起支付所需的参数
|
|
|
+ PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request,requestPaymentAppid);
|
|
|
|
|
|
- //Call Unified Order API
|
|
|
- public void unifiedOrderTest() throws IOException {
|
|
|
- String unifiedOrderBody = String.join("\n" ,
|
|
|
- "{" ,
|
|
|
- "'sp_appid': 'wx2421b1c4370ec43b'," ,
|
|
|
- "'sp_mchid': '10000100'," ,
|
|
|
- "'sub_mchid': '20000100'," ,
|
|
|
- "'sub_appid': 'wx352671b037b437ec'," ,
|
|
|
- "'out_trade_no': '20150806125346'," ,
|
|
|
- "'merchant_category_code': '1011'," ,
|
|
|
- "'payer': {" ,
|
|
|
- "'sub_openid': 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o'" ,
|
|
|
- "}," ,
|
|
|
- "'notify_url': 'https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php'," ,
|
|
|
- "'trade_type': 'JSAPI'," ,
|
|
|
- "'amount': {" ,
|
|
|
- "'total': 10000," ,
|
|
|
- "'currency': 'HKD'" ,
|
|
|
- "}," ,
|
|
|
- "'attach': 'Payment test'," ,
|
|
|
- "'description': 'Miniprogramm Pay test'," ,
|
|
|
- "'detail': {" ,
|
|
|
- "'cost_price': 10000," ,
|
|
|
- "'receipt_id': '1234'," ,
|
|
|
- "'goods_detail': [{" ,
|
|
|
- "'goods_id': 'iphone6s_16G'," ,
|
|
|
- "'wxpay_goods_id': '1001'," ,
|
|
|
- "'goods_name': 'iPhone6s 16G'," ,
|
|
|
- "'quantity': 1," ,
|
|
|
- "'price': 528800" ,
|
|
|
- "}]" ,
|
|
|
- "}," ,
|
|
|
- "'scene_info': {" ,
|
|
|
- "'payer_client_ip': '14.23.150.211'," ,
|
|
|
- "'device_ip': '59.37.125.32'," ,
|
|
|
- "'device_id': '013467007045764'," ,
|
|
|
- "'operator_id': 'P001'," ,
|
|
|
- "'store_info': {" ,
|
|
|
- "'id': 'SZTX001'" ,
|
|
|
- "}" ,
|
|
|
- "}" ,
|
|
|
- "}").replace("'","\"");
|
|
|
- HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/transactions/jsapi");
|
|
|
- httpPost.addHeader("Accept", "application/json");
|
|
|
- httpPost.addHeader("Content-type", "application/json; charset=utf-8");
|
|
|
- httpPost.setEntity(new StringEntity(unifiedOrderBody));
|
|
|
- CloseableHttpResponse response = httpClient.execute(httpPost);
|
|
|
- //Process the response
|
|
|
+ // 5. 输出或返回给前端
|
|
|
+ System.out.println("package: " + response.getPackageVal());
|
|
|
+ System.out.println("nonceStr: " + response.getNonceStr());
|
|
|
+ System.out.println("timeStamp: " + response.getTimeStamp());
|
|
|
+ System.out.println("signType: " + response.getSignType());
|
|
|
+ System.out.println("paySign: " + response.getPaySign());
|
|
|
+ return CommonResult.success("ok");
|
|
|
+ }
|
|
|
+ @PostMapping(value = "/callback", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE, MediaType.APPLICATION_JSON_VALUE})
|
|
|
+ @PermitAll
|
|
|
+ @TenantIgnore
|
|
|
+ public CommonResult<String> weChatPayCallBack(){
|
|
|
+ System.out.println("wechat callback");
|
|
|
+ return CommonResult.success("callBackOk");
|
|
|
}
|
|
|
}
|