海珠营销型网站制作,微网站如何建设方案,企业网站建设毕业设计,怎样设计网页教程简介#xff1a;
本篇文章为多租户场景
在当今数字经济时代#xff0c;许多企业都面临着处理多租户支付的挑战。多租户系统是指一种架构#xff0c;其中单个实例的软件服务多个租户#xff0c;每个租户的数据通常被隔离#xff0c;以确保安全性和数据隐私。而在这种环境…简介
本篇文章为多租户场景
在当今数字经济时代许多企业都面临着处理多租户支付的挑战。多租户系统是指一种架构其中单个实例的软件服务多个租户每个租户的数据通常被隔离以确保安全性和数据隐私。而在这种环境下实现支付功能需要特别注意数据隔离、安全性和可扩展性等方面的考量。
本文将探讨如何利用JAVA编程语言实现多租户支付系统。我们将介绍一种简单而强大的解决方案该方案能够轻松处理多租户环境下的支付需求并且具有良好的可扩展性和安全性。
首先我们将深入了解多租户系统的特点以及为何需要专门的支付解决方案。然后我们将讨论设计和实施多租户支付系统的关键考虑因素包括数据隔离、安全认证、支付通道管理等。接着我们将介绍一些常用的JAVA支付库和框架以及它们在多租户环境下的应用实践。
通过本文的阅读读者将了解到如何利用JAVA技术栈构建一个稳健、高效的多租户支付系统并且能够灵活地适应不同规模和需求的业务场景。同时我们还将分享一些实用的技巧和最佳实践帮助读者在实践中避免常见的问题和挑战。
无论您是正在构建多租户支付系统的开发者还是对多租户系统和支付技术感兴趣的技术爱好者本文都将为您提供有价值的见解和指导助您在支付领域取得更大的成功。
pom依赖
微信支付pom
代码部分
判断是否存在该支付类型(根据service Bean名称)
String BASE_NAME PayStrategy;/*** 获取支付凭证*/static MapString, Object ObtainCertificate(String orderSerial, PaymentBody paymentBody, PaymentConfigProperties paymentConfigProperties, HttpServletResponse response) {// 支付类型与付款方式String paymentType paymentBody.getPaymentType();String appName paymentConfigProperties.getAppName();String beanName appName BASE_NAME;if (!SpringUtils.containsBean(beanName)) {throw new ResultException(支付类型不正确!);}IPayStrategy instance SpringUtils.getBean(beanName);instance.validate(paymentBody);return instance.ObtainCertificate(paymentType, orderSerial, paymentBody, paymentConfigProperties, response);}
service
注⚠️多支付渠道情境下需要配置路由Bean名称
Service(wechatV3 IPayStrategy.BASE_NAME)传入paymentType(支付渠道)
PaymentBody中 为订单信息 以及租户信息和应用信息 Overridepublic MapString, Object ObtainCertificate(String paymentType, String orderSerial, PaymentBody paymentBody, PaymentConfigProperties paymentConfigProperties, HttpServletResponse response) {switch (paymentType) {case Const.APP:return wxPay(paymentBody, paymentConfigProperties,WXPayConstants.V3_APP_API,Const.APP);case Const.JSAPI:return wxPay(paymentBody, paymentConfigProperties,WXPayConstants.V3_JSAPI_API,Const.JSAPI);default:log.error(不支持的支付类型:{}, paymentType);throw new ResultException(不支持的支付类型);}}
impl
public MapString, Object wxPay(PaymentBody paymentBody, PaymentConfigProperties paymentConfigProperties,String api,String type) {MapString,Object map new HashMap();//支付总金额BigDecimal totalPrice BigDecimal.ZERO;totalPrice totalPrice.add(BigDecimal.valueOf(paymentBody.getActualPrice()).divide(BigDecimal.valueOf(100)));//转换金额保留两位小数点Integer moneynew BigDecimal(String.valueOf(totalPrice)).movePointRight(2).intValue();try {//验证证书CloseableHttpClient httpClient wxPayV3Util.checkSign(paymentConfigProperties);//app下单HttpPost httpPost new HttpPost(api);httpPost.addHeader(Accept, application/json);httpPost.addHeader(Content-type, application/json; charsetutf-8);ByteArrayOutputStream bos new ByteArrayOutputStream();ObjectMapper objectMapper new ObjectMapper();ObjectNode rootNode objectMapper.createObjectNode();rootNode.put(mchid, paymentConfigProperties.getMchId()).put(appid, paymentConfigProperties.getAppId()).put(description,paymentBody.getDescription()).put(notify_url, paymentConfigProperties.getNotifyUrl())//回调.put(out_trade_no, paymentBody.getOrderNo());// 如果为JSAPI支付if (type.equals(Const.JSAPI)){if (StringUtils.isBlank(paymentBody.getOpenId())){throw new IllegalStateException(openId不能为空);}rootNode.putObject(payer).put(openid, paymentBody.getOpenId());}rootNode.putObject(amount).put(total,paymentBody.getActualPrice());objectMapper.writeValue(bos, rootNode);httpPost.setEntity(new StringEntity(bos.toString(UTF-8), UTF-8));//完成签名并执行请求CloseableHttpResponse response httpClient.execute(httpPost);//获取返回状态int statusCode response.getStatusLine().getStatusCode();log.info(JSAPI成功-{}-{},statusCode,response);if (statusCode 200) { //处理成功String result EntityUtils.toString(response.getEntity(), UTF-8);JSONObject object JSONObject.parseObject(result);//获取预付单String prepayId object.getString(prepay_id);//生成签名Long timestamp System.currentTimeMillis() / 1000;//随机字符串 这个是微信支付maven自带的 也可以用其它的//这个是v2支付依赖自带的工具包 可以去掉了//String nonceStr WXPayUtil.generateNonceStr();//该方法org.apache.commons.lang3.RandomStringUtils依赖自带随机生成字符串 RandomStringUtils.randomAlphanumeric(32) 代表生成32位String nonceStr RandomStringUtils.randomAlphanumeric(32);//生成带签名支付信息String paySign wxPayV3Util.appPaySign(String.valueOf(timestamp), nonceStr, prepayId, paymentConfigProperties);MapString, String param new HashMap();map.put(apply, paymentBody.getApply());param.put(appid, paymentConfigProperties.getAppId());param.put(partnerid, paymentConfigProperties.getMchId());param.put(prepayid, prepayId);param.put(package, SignWXPay);param.put(noncestr, nonceStr);param.put(timestamp, String.valueOf(timestamp));param.put(sign, paySign);map.put(code,200);map.put(message, 下单成功);map.put(data, param);return map;}else {map.put(code, statusCode);map.put(message, 下单失败);map.put(data, EntityUtils.toString(response.getEntity(), UTF-8)); // 提取响应体信息return map;}} catch (Exception e) {log.error(微信预支付id获取失败{}-{},e.getMessage(),e);e.printStackTrace();}return map;}
获取预支付id
注⚠️JSAPI拉起支付时 需要传入 用户的openid
获取方式在我的另一个文章 获取openid
WxPayV3Util
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;Component
public class WxPayV3Util {/*** 证书验证* 自动更新的签名验证器*/public CloseableHttpClient checkSign(PaymentConfigProperties paymentConfigProperties) throws IOException {//验签CloseableHttpClient httpClient null;PrivateKey merchantPrivateKey getPrivateKey(paymentConfigProperties);httpClient WechatPayHttpClientBuilder.create().withMerchant(paymentConfigProperties.getMchId(), paymentConfigProperties.getMchSerialNo(), merchantPrivateKey).withValidator(new WechatPay2Validator(getVerifier(paymentConfigProperties))).build();return httpClient;}/*** 保存微信平台证书*/private final ConcurrentHashMapString, AutoUpdateCertificatesVerifier verifierMap new ConcurrentHashMap();/*** 功能描述:获取平台证书自动更新* 注意这个方法内置了平台证书的获取和返回值解密*/public AutoUpdateCertificatesVerifier getVerifier(PaymentConfigProperties properties) {String mchSerialNo properties.getMchSerialNo();AutoUpdateCertificatesVerifier verifier null;if (verifierMap.isEmpty() || !verifierMap.containsKey(mchSerialNo)) {verifierMap.clear();try {//传入证书PrivateKey privateKey getPrivateKey(properties);//刷新PrivateKeySigner signer new PrivateKeySigner(mchSerialNo, privateKey);WechatPay2Credentials credentials new WechatPay2Credentials(properties.getMchId(), signer);verifier new AutoUpdateCertificatesVerifier(credentials, properties.getApiV3Key().getBytes(utf-8));verifierMap.put(verifier.getValidCertificate().getSerialNumber(), verifier);} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (IOException e) {throw new RuntimeException(e);}} else {verifier verifierMap.get(mchSerialNo);}return verifier;}/*** app生成带签名支付信息** param timestamp 时间戳* param nonceStr 随机数* param prepayId 预付单* return 支付信息* throws Exception*/public String appPaySign(String timestamp, String nonceStr, String prepayId,PaymentConfigProperties paymentConfigProperties) throws Exception {//上传私钥PrivateKey privateKey getPrivateKey(paymentConfigProperties);String signatureStr Stream.of(paymentConfigProperties.getAppId(), timestamp, nonceStr, prepayId).collect(Collectors.joining(\n, , \n));Signature sign Signature.getInstance(SHA256withRSA);sign.initSign(privateKey);sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));return Base64.getEncoder().encodeToString(sign.sign());}/*** 小程序及其它支付生成带签名支付信息** param timestamp 时间戳* param nonceStr 随机数* param prepayId 预付单* return 支付信息* throws Exception*/public String jsApiPaySign(String timestamp, String nonceStr, String prepayId,PaymentConfigProperties paymentConfigProperties) throws Exception {//上传私钥PrivateKey privateKey getPrivateKey(paymentConfigProperties);String signatureStr Stream.of(paymentConfigProperties.getAppId(), timestamp, nonceStr, prepay_idprepayId).collect(Collectors.joining(\n, , \n));Signature sign Signature.getInstance(SHA256withRSA);sign.initSign(privateKey);sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));return Base64.getEncoder().encodeToString(sign.sign());}/*** 获取私钥。* String filename 私钥文件路径 (required)* return 私钥对象*/public PrivateKey getPrivateKey(PaymentConfigProperties properties) throws IOException {String content new String(Files.readAllBytes(Paths.get(properties.getPrivateKeyPath())), 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(无效的密钥格式);}}}
PaymentConfigProperties import lombok.Data;
import lombok.ToString;/*** 支付配置*/
Data
ToString
public class PaymentConfigProperties {/*** 是否启用*/private Boolean enabled;/*** 应用配置名称alipaywechatyinsheng*/private String appName;/*** 支持的付款类型*/private String paymentType;/*** 微信公众号或者小程序等的appid*/private String appId;/*** 微信支付商户号*/private String mchId;/*** 公钥*/private String publicKey;/*** 微信支付商户密钥支付宝私钥*/private String privateKey;private String privateKeyPath;/*** 服务商模式下的子商户公众账号ID* 如果是普通模式请不要配置这两个参数最好从配置文件中移除相关项*/private String subAppId;/*** 服务商模式下的子商户号* 如果是普通模式请不要配置这两个参数最好从配置文件中移除相关项*/private String subMchId;/*** 微信p12证书的位置可以指定绝对路径也可以指定类路径以classpath:开头* 支付宝应用公钥证书文件本地路径。*/private String keyPath;/*** 支付成功回调地址*/private String notifyUrl;/*** 支付成功返回地址*/private String returnUrl;/*** 退款成功回调地址*/private String refundUrl;//-----------支付宝-----------/*** 支付宝证书*/private String appCertPath;/*** 支付宝证书*/private String aliPayCertPath;/*** 支付宝证书*/private String aliPayRootCertPath;/****/private String domain;/*** 支付宝服务地址*/private String serverUrl;/*** 图标*/private String icon;/*** 平台证书序列号*/private String mchSerialNo;/*** API V3密钥 c3Gmjlc5A7fds7uJNUsReVEbs37BlmWL;*/private String apiV3Key;/*** 银盛私钥*/private String ysPrivateCerPath;/*** 银盛公钥*/private String ysPublicCerPath;/*** 银盛环境*/private String ysEnv;/*** 银盛私钥密码*/private String ysPrivateCerPwd;/*** 银盛发起方*/private String srcMerchantNo;/*** 银盛收款方*/private String payeeMerchantNo;/*** 银盛支付回调地址*/private String ysNotifyUrl;/*** 银盛支付回调地址*/private String ysRefundUrl;/*** v2 微信 商户key*/private String mchKey;/** 拉卡拉 银联商户号 */private String lklMerchantNo;/** 拉卡拉 订单有效期 */private int lklOrderEffMinutes 15;/** 拉卡拉 订单支付成功后商户接收订单通知的地址 */private String lklOrderCreateNotifyUrl;}PayProperties
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;import java.util.Map;/*** payment 配置属性*/
Data
Component
ConfigurationProperties(prefix payment)
public class PayProperties {/*** 支付租户*/private MapString, MapString, PaymentConfigProperties tenement;}Const
public class Const {public static final String NATIVE NATIVE;//原生扫码支付public static final String APP APP;//APP支付public static final String JSAPI JSAPI;//公众号支付/小程序支付public static final String M_WEB MWEB;//H5支付public static final String MICRO_PAY MICROPAY;//刷卡支付public static final String PC PC;//PC支付public static final String WAP WAP;//网页支付
}到这里 就可以正常的获取到微信的预支付id了