package net.shop.system.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import net.shop.framework.common.utils.Result;
import net.shop.system.query.SysAliPayQuery;
import net.shop.system.utils.HttpClientUtil;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import okhttp3.HttpUrl;
import java.security.PrivateKey;
import java.security.Signature;
@RestController
@RequestMapping("sys/wechat")
@Tag(name="微信付款码支付")
@AllArgsConstructor
public class SysWechatPayController {
protected static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
protected static final SecureRandom RANDOM = new SecureRandom();
protected static final String certificateSerialNumber = "XXXXXX";
protected static final String merchantId = "XXXXXX";
public String getToken(String method, HttpUrl url, String body) throws UnsupportedEncodingException {
String nonceStr = generateNonceStr();
long timestamp = System.currentTimeMillis() / 1000;
String message = buildMessage(method, url, timestamp, nonceStr, body);
String signature = sign(message.getBytes("utf-8"));
return "mchid=\"" + merchantId + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "serial_no=\"" + certificateSerialNumber + "\","
+ "signature=\"" + signature + "\"";
}
public String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
String canonicalUrl = url.encodedPath();
if (url.encodedQuery() != null) {
canonicalUrl += "?" + url.encodedQuery();
}
return method + "\n"
+ canonicalUrl + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ body + "\n";
}
public PrivateKey getPrivateKey(String filename) throws IOException {
String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
try {
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效的密钥格式");
}
}
public String sign(byte[] message) {
try {
PrivateKey privateKey = getPrivateKey("D:\\project\\shop\\cert\\apiclient_key.pem");
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(privateKey);
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
} catch (SignatureException e) {
throw new RuntimeException("签名计算失败", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("无效的私钥", e);
} catch (IOException e) {
throw new RuntimeException("无效的密钥格式", e);
}
}
protected long generateTimestamp() {
return System.currentTimeMillis() / 1000;
}
protected 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);
}
@PostMapping("userPay")
@Operation(summary = "用户支付")
public Result userPay(@RequestBody @Valid SysAliPayQuery query){
try{
String json= "{";
json+="\"appid\" : \"wxf46b8243f657189f\",";
json+="\"mchid\" : \"1676902789\",";
json+="\"description\" : \""+query.getTitle()+"\",";
json+="\"out_trade_no\" : \""+query.getOrderNum()+"\",";
json+="\"support_fapiao\" : false,";
json+="\"payer\" : {";
json+="\"auth_code\" : \""+query.getAuthCode()+"\"";
json+="},";
json+="\"amount\" : {";
json+="\"total\" : 100,";
json+="\"currency\" : \"CNY\"";
json+="},";
json+="\"scene_info\" : {";
json+="\"store_info\" : {";
json+="\"id\" : \"0001\",";
json+="\"out_id\" : \"111112\"";
json+="}";
json+="},";
json+="\"detail\" : {";
json+="\"goods_detail\" : [";
json+="{";
json+="\"merchant_goods_id\" : \"1246464644\",";
json+="\"wxpay_goods_id\" : \"1001\",";
json+="\"goods_name\" : \""+query.getTitle()+"\",";
json+="\"quantity\" : 1,";
json+="\"unit_price\" : 100";
json+="}";
json+="]";
json+="}";
json+="}";
HttpUrl httpurl = HttpUrl.parse("https://api.mch.weixin.qq.com/v3/certificates");
Map headers = new HashMap<>();
headers.put("Authorization","WECHATPAY2-SHA256-RSA2048 "+getToken("GET", httpurl, ""));
headers.put("Accept","application/json");
headers.put("Content-Type","application/json; charset=utf-8");
String url1 = "https://api.mch.weixin.qq.com/v3/pay/transactions/codepay";
HttpClientUtil.postJson(url1, null, headers, json,false);
return Result.ok();
}
catch (Exception ex){
return Result.error("系统内部错误!");
}
}
}
建议直接用sdkhttps://github.com/wechatpay-apiv3/wechatpay-java