微信支付集成

pull/1/head
sjm 2 years ago
parent 261614e2d6
commit 98b094eaa4

@ -1,8 +1,10 @@
package com.ruoyi; package com.ruoyi;
import com.cyl.wechat.WechatPayData;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
/** /**
* *
@ -13,6 +15,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
exclude = {DataSourceAutoConfiguration.class}, exclude = {DataSourceAutoConfiguration.class},
scanBasePackages = {"com.ruoyi", "com.cyl"} scanBasePackages = {"com.ruoyi", "com.cyl"}
) )
@EnableConfigurationProperties(WechatPayData.class)
public class RuoYiApplication { public class RuoYiApplication {
public static void main(String[] args) { public static void main(String[] args) {
// System.setProperty("spring.devtools.restart.enabled", "false"); // System.setProperty("spring.devtools.restart.enabled", "false");

@ -45,6 +45,11 @@ aliyun:
wechat: wechat:
appId: 你的微信服务号信息 appId: 你的微信服务号信息
secret: 你的微信服务号信息 secret: 你的微信服务号信息
merchantId:
privateKeyPath:
merchantSerialNumber:
apiV3key:
notifyUrl:
sms: sms:
enabled: true enabled: true
# 阿里云 dysmsapi.aliyuncs.com # 阿里云 dysmsapi.aliyuncs.com

@ -161,6 +161,12 @@ extra:
redirect: "" redirect: ""
scheduling: scheduling:
enabled: true enabled: true
http-pool:
connection-request-timeout: 10000
connection-timeout: 10000
socket-timeout: 10000
max-per-route: 200
max-total: 200
gen: gen:
# 作者 # 作者
author: author:

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEKzCCAxOgAwIBAgIUW0mbrKuZqAqNv3ZTP/ylJOrmuIAwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjMwNzEwMDMwMDA2WhcNMjgwNzA4MDMwMDA2WjCBhDETMBEGA1UEAwwK
MTY0MDE2NTIyOTEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTAwLgYDVQQL
DCfoi4/lt57or5rkuI7kuZDkv6Hmga/mioDmnK/mnInpmZDlhazlj7gxCzAJBgNV
BAYMAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBALrELkqmSy8PkSk3KL3eNwQKm08xvIsrk3KKuyS7YrulQaKmqfvn
Nh0qXfgFYCqOKlV6VBEAxSRfsNq1/NsVCa5ZGcrcHlf0iC22p81w0W0TTT4LzfRj
tuobO/uhLhtHHaWxyVdloRirHEl5xXREvI6xstjEEG5vYyIFxR2Aufpv6e20FpZ3
G3KiTSRFoSSHMJjOoPEeGd6nAzueqGGjdGcxNHl/a9HDyYcRMeIO9k1Wd1gcwwpu
vinELhB1ojpzO6UekAzjM4g7QD2aAibmb0/Q+4hFw7xXOrOElt0hjKcoG+hZGlFL
xjLCC+Z/xbLEz1jtH7mAdUG/g2Ls4pr470UCAwEAAaOBuTCBtjAJBgNVHRMEAjAA
MAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2
Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJD
MDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJC
MjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQBK
udnDDPqczbrX8RmKcw5rixMyM5toyMtqNR1pfkWCG2QP2PmNTYBARO2tW2DZtWZm
eLuUXZVE12FnIkEQQ65JmDEEvz8PwTSQIPCcObHUGKuj5dFWswzYEWMsqIdPfGK+
Teb294FsTRiElDMkpt3pmYkGVxFwsAfSFZM/9Gl45HdagD4RHfvRDgB8WGx6azxX
w/kn8CHJqLEJtbPer8cGFCih0v0LfLCy4MEHb0Si0PWoEmecTE3DtXK5gEKLFlEy
g1Gb16LW1eEGwyfqcUmwXDE3aamL4RpxjZgzIagvsqVfPn7nDiw6fFf78Ya0ClVx
ImJDoCe+5aH652BNZXWD
-----END CERTIFICATE-----

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC6xC5KpksvD5Ep
Nyi93jcECptPMbyLK5Nyirsku2K7pUGipqn75zYdKl34BWAqjipVelQRAMUkX7Da
tfzbFQmuWRnK3B5X9IgttqfNcNFtE00+C830Y7bqGzv7oS4bRx2lsclXZaEYqxxJ
ecV0RLyOsbLYxBBub2MiBcUdgLn6b+nttBaWdxtyok0kRaEkhzCYzqDxHhnepwM7
nqhho3RnMTR5f2vRw8mHETHiDvZNVndYHMMKbr4pxC4QdaI6czulHpAM4zOIO0A9
mgIm5m9P0PuIRcO8VzqzhJbdIYynKBvoWRpRS8Yywgvmf8WyxM9Y7R+5gHVBv4Ni
7OKa+O9FAgMBAAECggEAKlMzOm+dMjkQ6Io7jWvChAPzVmsrE074x0hxSM2+fk/h
I/8pHpEzTkC/sulk9b/qEBz82C2Yf7m/1pDPkMafvwcqNTLVHZGpGtL+DCy5CUMK
ijkan3vJhBxP56KLVFs1eMtlmYzKiVCdxHj4KnU5Vg0nPsCQCLbtfFRaa2k0vHAl
LpaC+HG/nFMewTC9NBcfu+IMzs5miUgvWpkAVXvq4WcJq+ACsnc2rzDlEa7ZNaNR
3CtHCj+f7Yhnt+U+IImIGLeh2Z1wR7dCxnuAQ02W0pWl8pIUmrL8m9LwRrgL1Rje
Fp1DXTXKlCl3oHELz9qHfJ06K2r5laCqMV2s6esTgQKBgQDfNnev21ydMe7F6NaO
0s1Xb4Btl9NSiCEoTjdt/w2rCFdvu0YsgutaB0J4rk2Jeyp7MlHiYkUn1X/dJlMX
/P/hmgtnnFJotIvHFl3nHem8l10mB+MYCeI65uDOvEcXoGV+jMTZd4wzJlSC/uio
UbsWf7ykHMW8M77fbzncY5MOiQKBgQDWMzU0NfRdVE3Y7c7vfJr2jMPhkB5ai5Cr
PK+kCbVKALR79C4aPaUM3RmYaG61NPaylw1+czv1YxWvtiHidiGAM3t/18qbKicf
SQxqe4uFAXWMafopThPLw9KGq0sSjOs9DDt8CIscAzAs9ykj2DT2khfAeqZdwjDo
CVnWKtmL3QKBgQDRYMNGd2tZnPQQO0e/82dgpBwBMVCt8zm/GBeQm5YToCB8k1vQ
9wGTkom8sCvVUW5Y36vFwk6CfheRt5hsQQs5cQlPqGf5BZq0JnvxBrMxD3715KIV
83d9rwKjiiLZu8BYw+0G3MfrVwIhWsGc2gW3phyqiL7GundKGYhZ/iF8mQKBgATd
BMg6sWGtGFdkjt5BJgOTDp+Adi/4G0DID/TZg5Q6j96AnMtbuvOf0YT0Wg8jNLa5
V0UXuLTJmyRyLjLGG9ydTqCIdcEwI9NCBVYll/VdIx4dCeEGNMzblVwZZ4r0SCMK
tA8M4puwNtfOhNCbiBhRGNj17/ERB6s9Cx0hu+7BAoGAV3nfA3JACh30oStpZQab
CY3JJvqIFINGk1h8cY9yjlI/g6WXw6ACtIVhvy9bEOkSFgtMdCFWVLC/E836p2Qo
38J4DLiXszt+4y8PFviVszVFZIRkHOOluRXSedDmPteu4bQCCXO4MCzC502PKcyM
X38MmpqzqQxZoe5PbwJENR0=
-----END PRIVATE KEY-----

@ -6,6 +6,9 @@ import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES; import cn.hutool.crypto.symmetric.AES;
import com.cyl.manager.ums.service.MemberCartService; import com.cyl.manager.ums.service.MemberCartService;
import com.cyl.wechat.WechatAuthService;
import com.cyl.wechat.WechatPayService;
import com.cyl.wechat.response.WechatUserAuth;
import com.ruoyi.RuoYiApplication; import com.ruoyi.RuoYiApplication;
import com.ruoyi.common.config.properties.SmsProperties; import com.ruoyi.common.config.properties.SmsProperties;
import com.ruoyi.common.core.sms.AliyunSmsTemplate; import com.ruoyi.common.core.sms.AliyunSmsTemplate;
@ -21,6 +24,7 @@ import org.springframework.test.context.junit4.SpringRunner;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = RuoYiApplication.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = RuoYiApplication.class)
@ -73,4 +77,18 @@ public class ServiceTest {
long id = snowflake.nextId(); long id = snowflake.nextId();
System.out.println("id:" + id); System.out.println("id:" + id);
} }
@Autowired
private WechatPayService wechatPayService;
@Autowired
private WechatAuthService wechatAuthService;
@Test
public void test5(){
// String code = "0611P2Ga1D8QCF0CVuJa1qNUJa11P2GL";
// WechatUserAuth userToken = wechatAuthService.getUserToken(code);
String openId="oUA8I6lDdwSfz-EwR4284dU3KOYw";
String res = wechatPayService.jsapiPay(UUID.randomUUID().toString().substring(0,30), "测试支付", 1, openId);
System.out.println(res);
}
} }

@ -23,6 +23,11 @@
<artifactId>ruoyi-system</artifactId> <artifactId>ruoyi-system</artifactId>
<version>${ruoyi.version}</version> <version>${ruoyi.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.9</version>
</dependency>
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId> <artifactId>mybatis-plus-boot-starter</artifactId>

@ -0,0 +1,73 @@
package com.cyl.config;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
//从连接池获取连接的超时时间,一般设置为较短;
@Value("${http-pool.connection-request-timeout}")
private int connectionRequestTimeout;
//连接超时时间
@Value("${http-pool.connection-timeout}")
private int connectionTimeout;
//完成连接后socket通信超时时间
@Value("${http-pool.socket-timeout}")
private int socketTimeout;
//单host可以理解为单域名最大并发数
@Value("${http-pool.max-per-route}")
private int maxPerRoute;
//连接池最大连接数
@Value("${http-pool.max-total}")
private int maxTotal;
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(httpRequestFactory());
}
@Bean
public ClientHttpRequestFactory httpRequestFactory() {
return new HttpComponentsClientHttpRequestFactory(httpClient());
}
@Bean
public HttpClient httpClient() {
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
//设置整个连接池最大连接数 根据自己的场景决定
connectionManager.setMaxTotal(maxTotal);
//路由是对maxTotal的细分
connectionManager.setDefaultMaxPerRoute(maxPerRoute);
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(socketTimeout)//服务器返回数据(response)的时间超过该时间抛出read timeout
.setConnectTimeout(connectionTimeout)//连接上服务器(握手成功)的时间超出该时间抛出connect timeout
.setConnectionRequestTimeout(connectionRequestTimeout)//从连接池中获取连接的超时时间超过该时间未拿到可用连接会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
.build();
return HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(connectionManager)
.build();
}
}

@ -0,0 +1,44 @@
package com.cyl.wechat;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.cyl.wechat.response.WechatUserAuth;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
@Service
@Slf4j
public class WechatAuthService {
@Autowired
private RestTemplate restTemplate;
public WechatUserAuth getUserToken(String code) {
Map<String, String> params = new HashMap<>();
params.put("APPID", WechatPayData.appId);
params.put("SECRET", WechatPayData.secret);
params.put("CODE", code);
ResponseEntity<String> responseEntity = restTemplate.getForEntity(
"https://api.weixin.qq.com/sns/oauth2/access_token?appid={APPID}&secret={SECRET}&code={CODE}&grant_type=authorization_code",
String.class, params);
String body = responseEntity.getBody();
try {
WechatUserAuth object = JSON.parseObject(body, WechatUserAuth.class);
if (object == null) {
log.error("获取user wechat accesstoken失败");
return null;
}
log.info("get user wechat accesstoken{}",JSONUtil.toJsonStr(object));
return object;
} catch (Exception e) {
log.info("get user wechat accesstoken error",e);
}
return null;
}
}

@ -0,0 +1,27 @@
package com.cyl.wechat;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
public class WechatPayConfig {
private static Config wechatPayConfig;
private WechatPayConfig(){}
public static Config getInstance() {
if (wechatPayConfig == null) {
wechatPayConfig = new RSAAutoCertificateConfig.Builder()
.merchantId(WechatPayData.merchantId)
.privateKeyFromPath(WechatPayConfig.class.getResource(WechatPayData.privateKeyPath).getPath())
.merchantSerialNumber(WechatPayData.merchantSerialNumber)
.apiV3Key(WechatPayData.apiV3key)
.build();
}
return wechatPayConfig;
}
}

@ -0,0 +1,49 @@
package com.cyl.wechat;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "wechat")
public class WechatPayData {
/** 商户号 */
public static String appId;
public static String secret;
public static String merchantId;
/** 商户API私钥路径 */
public static String privateKeyPath;
/** 商户证书序列号 */
public static String merchantSerialNumber;
/** 商户APIV3密钥 */
public static String apiV3key;
public static String notifyUrl;
public void setAppId(String appId) {
WechatPayData.appId = appId;
}
public void setSecret(String secret) {
WechatPayData.secret = secret;
}
public void setMerchantId(String merchantId) {
WechatPayData.merchantId = merchantId;
}
public void setPrivateKeyPath(String privateKeyPath) {
WechatPayData.privateKeyPath = privateKeyPath;
}
public void setMerchantSerialNumber(String merchantSerialNumber) {
WechatPayData.merchantSerialNumber = merchantSerialNumber;
}
public void setApiV3key(String apiV3key) {
WechatPayData.apiV3key = apiV3key;
}
public void setNotifyUrl(String notifyUrl) {
WechatPayData.notifyUrl = notifyUrl;
}
}

@ -0,0 +1,41 @@
package com.cyl.wechat;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class WechatPayService {
public static final JsapiService service = new JsapiService.Builder().config(WechatPayConfig.getInstance()).build();
/**
* jsapi
* @param orderNo
* @param desc
* @param totalAmount
* @param openId openid
* @return prepay_id
*/
public String jsapiPay(String orderNo,String desc,Integer totalAmount,String openId){
PrepayRequest prepayRequest = new PrepayRequest();
prepayRequest.setAppid(WechatPayData.appId);
prepayRequest.setMchid(WechatPayData.merchantId);
prepayRequest.setDescription(desc);
prepayRequest.setOutTradeNo(orderNo);
prepayRequest.setNotifyUrl(WechatPayData.notifyUrl);
Amount amount = new Amount();
amount.setTotal(totalAmount);
prepayRequest.setAmount(amount);
Payer payer = new Payer();
payer.setOpenid(openId);
prepayRequest.setPayer(payer);
PrepayResponse response = service.prepay(prepayRequest);
return response.getPrepayId();
}
}

@ -0,0 +1,13 @@
package com.cyl.wechat.response;
import lombok.Data;
@Data
public class WechatUserAuth {
private String access_token;
private Integer expires_in;
private String refresh_token;
private String openid;
private String scope;
}
Loading…
Cancel
Save