15 changed files with 535 additions and 312 deletions
@ -1,143 +0,0 @@ |
|||
#<<<<<<< HEAD |
|||
##服务端点暴露 |
|||
#management: |
|||
# endpoints: |
|||
# web: |
|||
# exposure: |
|||
# # 暴露xxx端点,如需暴露多个,用,分隔;如需暴露所有端点,用'*' |
|||
# include: auditevents,caches,conditions,flyway,health,heapdump,httptrace,info,integrationgraph,jolokia,logfile,loggers,liquibase,metrics,mappings,prometheus,scheduledtasks,sessions,shutdown,threaddump,hystrix.stream |
|||
## # 不暴露哪些端点 |
|||
## exclude: env,beans,configprops |
|||
# endpoint: |
|||
# health: |
|||
# # 是否展示健康检查详情 |
|||
# show-details: always |
|||
# health: |
|||
# redis: |
|||
# enabled: false |
|||
##eureka注册 |
|||
#eureka: |
|||
# client: |
|||
# service-url: |
|||
# # 指定eureka server通信地址,注意/eureka/小尾巴不能少 |
|||
# #defaultZone: http://admin:admin@peer1:8761/eureka/,http://admin:admin@peer2:8762/eureka/ |
|||
## defaultZone: http://admin:admin@49.233.89.188:7010/eureka/ |
|||
# defaultZone: http://admin:admin@192.168.0.99:7010/eureka/ |
|||
# instance: |
|||
# # 是否注册IP到eureka server,如不指定或设为false,那就回注册主机名到eureka server |
|||
# prefer-ip-address: true |
|||
# metadata-map: |
|||
# management: |
|||
# context-path: ${server.servlet.context-path:}/actuator |
|||
# home-page-url-path: ${server.servlet.context-path:}/ |
|||
# status-page-url-path: ${server.servlet.context-path:}/actuator/info |
|||
# health-check-url-path: ${server.servlet.context-path:}/actuator/health |
|||
#feign: |
|||
# client: |
|||
# config: |
|||
# default: |
|||
# connectTime: 5000 |
|||
# readTimeout: 5000 |
|||
# # NONE【性能最佳,适用于生产】:不记录任何日志(默认值)。 |
|||
# # BASIC【适用于生产环境追踪问题】:仅记录请求方法、URL、响应状态代码以及执行时间。 |
|||
# # HEADERS:记录BASIC级别的基础上,记录请求和响应的header。 |
|||
# # FULL【比较适用于开发及测试环境定位问题】:记录请求和响应的header、body和元数据 |
|||
# loggerLevel: basic |
|||
# hystrix: |
|||
# enabled: true |
|||
## sleuth |
|||
#logging: |
|||
# level: |
|||
# root: info |
|||
# org.springframework.cloud.sleuth: DEBUG |
|||
#spring: |
|||
## zipkin: |
|||
## base-url: http://49.233.89.188:9411 |
|||
## sleuth: |
|||
## sampler: |
|||
## # 采样率,模式0.1,也就是10%,为了便于观察效果,改为1.0,也就是100%。生产环境建议保持默认。 |
|||
## probability: 1.0 |
|||
# cloud: |
|||
# inetutils: |
|||
#======= |
|||
#服务端点暴露 |
|||
management: |
|||
endpoints: |
|||
web: |
|||
exposure: |
|||
# 暴露xxx端点,如需暴露多个,用,分隔;如需暴露所有端点,用'*' |
|||
include: auditevents,caches,conditions,flyway,health,heapdump,httptrace,info,integrationgraph,jolokia,logfile,loggers,liquibase,metrics,mappings,prometheus,scheduledtasks,sessions,shutdown,threaddump,hystrix.stream |
|||
# # 不暴露哪些端点 |
|||
# exclude: env,beans,configprops |
|||
endpoint: |
|||
health: |
|||
# 是否展示健康检查详情 |
|||
show-details: always |
|||
health: |
|||
redis: |
|||
enabled: false |
|||
#eureka注册 |
|||
eureka: |
|||
client: |
|||
service-url: |
|||
# 指定eureka server通信地址,注意/eureka/小尾巴不能少 |
|||
#defaultZone: http://admin:admin@peer1:8761/eureka/,http://admin:admin@peer2:8762/eureka/ |
|||
defaultZone: http://admin:admin@49.232.6.143:7010/eureka/ |
|||
# defaultZone: http://admin:admin@192.168.0.99:7010/eureka/ |
|||
# defaultZone: http://admin:admin@test.tall.wiki:7010/eureka/ |
|||
instance: |
|||
# 是否注册IP到eureka server,如不指定或设为false,那就回注册主机名到eureka server |
|||
prefer-ip-address: true |
|||
metadata-map: |
|||
management: |
|||
context-path: ${server.servlet.context-path:}/actuator |
|||
home-page-url-path: ${server.servlet.context-path:}/ |
|||
status-page-url-path: ${server.servlet.context-path:}/actuator/info |
|||
health-check-url-path: ${server.servlet.context-path:}/actuator/health |
|||
feign: |
|||
client: |
|||
config: |
|||
default: |
|||
connectTime: 5000 |
|||
readTimeout: 5000 |
|||
# NONE【性能最佳,适用于生产】:不记录任何日志(默认值)。 |
|||
# BASIC【适用于生产环境追踪问题】:仅记录请求方法、URL、响应状态代码以及执行时间。 |
|||
# HEADERS:记录BASIC级别的基础上,记录请求和响应的header。 |
|||
# FULL【比较适用于开发及测试环境定位问题】:记录请求和响应的header、body和元数据 |
|||
loggerLevel: basic |
|||
hystrix: |
|||
enabled: true |
|||
hystrix: |
|||
threadpool: |
|||
default: |
|||
coreSize: 200 #并发执行的最大线程数,默认10 |
|||
maxQueueSize: 1000 #BlockingQueue的最大队列数,默认值-1 |
|||
queueSizeRejectionThreshold: 800 #即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝,默认值5 |
|||
command: |
|||
default: |
|||
execution: |
|||
timeout: |
|||
enabled: true |
|||
isolation: |
|||
strategy: SEMAPHORE |
|||
thread: |
|||
timeoutInMilliseconds: 60000 |
|||
#ribbon的超时时间 |
|||
ribbon: |
|||
ReadTimeout: 60000 |
|||
ConnectTimeout: 60000 |
|||
# sleuth |
|||
logging: |
|||
level: |
|||
root: info |
|||
org.springframework.cloud.sleuth: DEBUG |
|||
spring: |
|||
# zipkin: |
|||
# base-url: http://49.233.89.188:9411 |
|||
# sleuth: |
|||
# sampler: |
|||
# # 采样率,模式0.1,也就是10%,为了便于观察效果,改为1.0,也就是100%。生产环境建议保持默认。 |
|||
# probability: 1.0 |
|||
cloud: |
|||
inetutils: |
|||
ignored-interfaces: ['VMware.*'] |
@ -1,66 +0,0 @@ |
|||
#服务端点暴露 |
|||
management: |
|||
endpoints: |
|||
web: |
|||
exposure: |
|||
# 暴露xxx端点,如需暴露多个,用,分隔;如需暴露所有端点,用'*' |
|||
include: auditevents,caches,conditions,flyway,health,heapdump,httptrace,info,integrationgraph,jolokia,logfile,loggers,liquibase,metrics,mappings,prometheus,scheduledtasks,sessions,shutdown,threaddump,hystrix.stream |
|||
# # 不暴露哪些端点 |
|||
# exclude: env,beans,configprops |
|||
endpoint: |
|||
health: |
|||
# 是否展示健康检查详情 |
|||
show-details: always |
|||
health: |
|||
redis: |
|||
enabled: false |
|||
#eureka注册 |
|||
eureka: |
|||
client: |
|||
service-url: |
|||
# 指定eureka server通信地址,注意/eureka/小尾巴不能少 |
|||
defaultZone: http://admin:admin@82.157.24.76:7010/eureka/ |
|||
# defaultZone: http://admin:admin@49.232.6.143:7010/eureka/ |
|||
instance: |
|||
# 是否注册IP到eureka server,如不指定或设为false,那就回注册主机名到eureka server |
|||
prefer-ip-address: true |
|||
metadata-map: |
|||
management: |
|||
context-path: ${server.servlet.context-path:}/actuator |
|||
home-page-url-path: ${server.servlet.context-path:}/ |
|||
status-page-url-path: ${server.servlet.context-path:}/actuator/info |
|||
health-check-url-path: ${server.servlet.context-path:}/actuator/health |
|||
feign: |
|||
client: |
|||
config: |
|||
default: |
|||
connectTime: 5000 |
|||
readTimeout: 5000 |
|||
# NONE【性能最佳,适用于生产】:不记录任何日志(默认值)。 |
|||
# BASIC【适用于生产环境追踪问题】:仅记录请求方法、URL、响应状态代码以及执行时间。 |
|||
# HEADERS:记录BASIC级别的基础上,记录请求和响应的header。 |
|||
# FULL【比较适用于开发及测试环境定位问题】:记录请求和响应的header、body和元数据 |
|||
loggerLevel: basic |
|||
hystrix: |
|||
enabled: true |
|||
hystrix: |
|||
command: |
|||
default: |
|||
execution: |
|||
isolation: |
|||
strategy: SEMAPHORE |
|||
thread: |
|||
timeoutInMilliseconds: 60000 |
|||
#ribbon的超时时间 |
|||
ribbon: |
|||
ReadTimeout: 60000 |
|||
ConnectTimeout: 60000 |
|||
# sleuth |
|||
logging: |
|||
level: |
|||
root: info |
|||
org.springframework.cloud.sleuth: DEBUG |
|||
spring: |
|||
cloud: |
|||
inetutils: |
|||
ignored-interfaces: ['VMware.*'] |
@ -1,82 +0,0 @@ |
|||
#服务端点暴露 |
|||
management: |
|||
endpoints: |
|||
web: |
|||
exposure: |
|||
# 暴露xxx端点,如需暴露多个,用,分隔;如需暴露所有端点,用'*' |
|||
include: auditevents,caches,conditions,flyway,health,heapdump,httptrace,info,integrationgraph,jolokia,logfile,loggers,liquibase,metrics,mappings,prometheus,scheduledtasks,sessions,shutdown,threaddump,hystrix.stream |
|||
# # 不暴露哪些端点 |
|||
exclude: env,beans,configprops |
|||
endpoint: |
|||
health: |
|||
# 是否展示健康检查详情 |
|||
show-details: always |
|||
health: |
|||
redis: |
|||
enabled: false |
|||
#eureka注册 |
|||
eureka: |
|||
client: |
|||
service-url: |
|||
# 指定eureka server通信地址,注意/eureka/小尾巴不能少 |
|||
#defaultZone: http://admin:admin@peer1:8761/eureka/,http://admin:admin@peer2:8762/eureka/ |
|||
|
|||
# defaultZone: http://admin:admin@81.70.54.64:7010/eureka/ |
|||
defaultZone: http://admin:admin@121.36.3.207:7010/eureka/ |
|||
|
|||
instance: |
|||
# 是否注册IP到eureka server,如不指定或设为false,那就回注册主机名到eureka server |
|||
prefer-ip-address: true |
|||
metadata-map: |
|||
management: |
|||
context-path: ${server.servlet.context-path:}/actuator |
|||
home-page-url-path: ${server.servlet.context-path:}/ |
|||
status-page-url-path: ${server.servlet.context-path:}/actuator/info |
|||
health-check-url-path: ${server.servlet.context-path:}/actuator/health |
|||
feign: |
|||
client: |
|||
config: |
|||
default: |
|||
connectTime: 5000 |
|||
readTimeout: 5000 |
|||
# NONE【性能最佳,适用于生产】:不记录任何日志(默认值)。 |
|||
# BASIC【适用于生产环境追踪问题】:仅记录请求方法、URL、响应状态代码以及执行时间。 |
|||
# HEADERS:记录BASIC级别的基础上,记录请求和响应的header。 |
|||
# FULL【比较适用于开发及测试环境定位问题】:记录请求和响应的header、body和元数据 |
|||
loggerLevel: basic |
|||
hystrix: |
|||
enabled: true |
|||
hystrix: |
|||
threadpool: |
|||
default: |
|||
coreSize: 200 #并发执行的最大线程数,默认10 |
|||
maxQueueSize: 1000 #BlockingQueue的最大队列数,默认值-1 |
|||
queueSizeRejectionThreshold: 800 #即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝,默认值5 |
|||
command: |
|||
default: |
|||
execution: |
|||
timeout: |
|||
enabled: true |
|||
isolation: |
|||
strategy: SEMAPHORE |
|||
thread: |
|||
timeoutInMilliseconds: 60000 |
|||
#ribbon的超时时间 |
|||
ribbon: |
|||
ReadTimeout: 60000 |
|||
ConnectTimeout: 60000 |
|||
# sleuth |
|||
logging: |
|||
level: |
|||
root: info |
|||
org.springframework.cloud.sleuth: DEBUG |
|||
spring: |
|||
# zipkin: |
|||
# base-url: http://140.143.228.3:9411 |
|||
# sleuth: |
|||
# sampler: |
|||
# # 采样率,模式0.1,也就是10%,为了便于观察效果,改为1.0,也就是100%。生产环境建议保持默认。 |
|||
# probability: 0.1 |
|||
cloud: |
|||
inetutils: |
|||
ignored-interfaces: ['VMware.*'] |
@ -0,0 +1,20 @@ |
|||
package com.ccsens.util.bean.wx.po; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author 逗 |
|||
*/ |
|||
@Data |
|||
public class WxApiTicket { |
|||
@JsonProperty("errcode") |
|||
private Integer errcode; |
|||
@JsonProperty("errmsg") |
|||
private String errmsg; |
|||
@JsonProperty("ticket") |
|||
private String ticket; |
|||
@JsonProperty("expires_in") |
|||
private Long expiresIn; |
|||
} |
@ -0,0 +1,120 @@ |
|||
package com.ccsens.wechatutil.bean.vo; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author 逗 |
|||
*/ |
|||
@Data |
|||
public class WxNativePay { |
|||
/** |
|||
* 应用ID 是 |
|||
*/ |
|||
private String appid; |
|||
/** |
|||
* 直连商户号 是 |
|||
*/ |
|||
private String mchid; |
|||
/** |
|||
* 商品描述 是 |
|||
*/ |
|||
private String description; |
|||
/** |
|||
* 商户订单号 是 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一 |
|||
*/ |
|||
private String out_trade_no; |
|||
/** |
|||
* 交易结束时间 否 |
|||
*/ |
|||
private String time_expire; |
|||
/** |
|||
* 附加数据 否 |
|||
*/ |
|||
private String attach; |
|||
/** |
|||
* 通知地址 是 |
|||
*/ |
|||
private String notify_url; |
|||
/** |
|||
* 订单优惠标记 否 |
|||
*/ |
|||
private String goods_tag; |
|||
/** |
|||
* 订单金额 是 |
|||
*/ |
|||
private Amount amount; |
|||
/** |
|||
* 优惠功能 |
|||
*/ |
|||
private Detail detail; |
|||
/** |
|||
* 场景信息 |
|||
*/ |
|||
private SceneInfo scene_info; |
|||
/** |
|||
* 结算信息 |
|||
*/ |
|||
private SceneInfo settle_info; |
|||
|
|||
@Data |
|||
public static class Amount{ |
|||
//总金额 是 单位为分。
|
|||
private int total; |
|||
//货币类型
|
|||
private String currency; |
|||
} |
|||
|
|||
@Data |
|||
public static class Detail{ |
|||
//订单原价 单位为分。
|
|||
private int cost_price; |
|||
//商品小票ID
|
|||
private String invoice_id; |
|||
//单品列表
|
|||
private List<GoodsDetail> goods_detail; |
|||
} |
|||
|
|||
@Data |
|||
public static class GoodsDetail{ |
|||
//商户侧商品编码 是
|
|||
private String merchant_goods_id; |
|||
//微信支付商品编码 否
|
|||
private String wechatpay_goods_id; |
|||
//商品名称
|
|||
private String goods_name; |
|||
//商品数量 是
|
|||
private int quantity; |
|||
//商品单价 是
|
|||
private int unit_price; |
|||
} |
|||
|
|||
@Data |
|||
public static class SceneInfo{ |
|||
//用户终端IP
|
|||
private String payer_client_ip; |
|||
//商户端设备号
|
|||
private String device_id; |
|||
//商户门店信息
|
|||
private StoreInfo store_info; |
|||
} |
|||
|
|||
@Data |
|||
public static class StoreInfo{ |
|||
//门店编号 是
|
|||
private String id; |
|||
//门店名称
|
|||
private String name; |
|||
//地区编码
|
|||
private String area_code; |
|||
//详细地址
|
|||
private String address; |
|||
} |
|||
|
|||
@Data |
|||
public static class SettleInfo{ |
|||
//是否指定分账
|
|||
private boolean profit_sharing; |
|||
} |
|||
} |
@ -0,0 +1,47 @@ |
|||
package com.ccsens.wechatutil.payutil.wxnative; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.alibaba.fastjson.JSON; |
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.ccsens.util.QrCodeUtil; |
|||
import com.ccsens.wechatutil.bean.vo.WxNativePay; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import okhttp3.HttpUrl; |
|||
|
|||
/** |
|||
* @author 逗 |
|||
*/ |
|||
@Slf4j |
|||
public class NativePay { |
|||
|
|||
/** |
|||
* 生成订单,返回收款二维码 |
|||
* @param serialNo 证书序列号 |
|||
* @param pathPem 秘钥文件地址 |
|||
* @param wxNativePay 订单信息 |
|||
* @param filePath 二维码存放地址(前缀路径) |
|||
* @return 返回二维码访问路径(不包含前缀) |
|||
*/ |
|||
public static String generateOrder( String serialNo, String pathPem, WxNativePay wxNativePay, String filePath){ |
|||
String path = ""; |
|||
try { |
|||
HttpUrl httpurl = HttpUrl.parse(NativeUtils.NATIVE_URL); |
|||
String body = String.valueOf(JSON.parseObject(JSON.toJSONString(wxNativePay))); |
|||
String authorization = NativeUtils.schema + " " + |
|||
NativeUtils.getToken("POST", httpurl, body, wxNativePay.getOut_trade_no(), wxNativePay.getMchid(), serialNo, pathPem); |
|||
//下单调用的接口,JSON格式
|
|||
String codeUrl = NativeUtils.nativePostBody(NativeUtils.NATIVE_URL, body, authorization); |
|||
log.info("调用微信下单返回:{}", codeUrl); |
|||
JSONObject jsonObject = JSONObject.parseObject(codeUrl); |
|||
String url = jsonObject.getString("code_url"); |
|||
if(StrUtil.isNotBlank(url)){ |
|||
path = QrCodeUtil.getQrCodeWithUtf8(url,filePath); |
|||
}else { |
|||
path = String.valueOf(jsonObject); |
|||
} |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return path; |
|||
} |
|||
} |
@ -0,0 +1,265 @@ |
|||
package com.ccsens.wechatutil.payutil.wxnative; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.google.zxing.BarcodeFormat; |
|||
import com.google.zxing.EncodeHintType; |
|||
import com.google.zxing.MultiFormatWriter; |
|||
import com.google.zxing.WriterException; |
|||
import com.google.zxing.client.j2se.MatrixToImageWriter; |
|||
import com.google.zxing.common.BitMatrix; |
|||
import okhttp3.HttpUrl; |
|||
import org.apache.commons.lang3.RandomStringUtils; |
|||
|
|||
import java.io.*; |
|||
import java.net.HttpURLConnection; |
|||
import java.net.URL; |
|||
import java.nio.file.FileSystems; |
|||
import java.nio.file.Files; |
|||
import java.nio.file.Path; |
|||
import java.nio.file.Paths; |
|||
import java.security.*; |
|||
import java.security.spec.InvalidKeySpecException; |
|||
import java.security.spec.PKCS8EncodedKeySpec; |
|||
import java.util.Base64; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import java.util.UUID; |
|||
|
|||
|
|||
/** |
|||
* @author 逗 |
|||
*/ |
|||
public class NativeUtils { |
|||
|
|||
static String schema = "WECHATPAY2-SHA256-RSA2048"; |
|||
|
|||
// /**
|
|||
// * 填写你的商户号
|
|||
// */
|
|||
// static String MCH_ID = "1624859575";
|
|||
|
|||
// /**
|
|||
// * 填写证书序列号
|
|||
// */
|
|||
// static String SERIAL_NO = "71433832548290D95806FAF490878BB9B2677D4E";
|
|||
|
|||
// /**
|
|||
// * 填写应用APP ID
|
|||
// */
|
|||
// static String APP_ID = "wxcb60fcfeaddeb3e3";
|
|||
//
|
|||
// /**
|
|||
// * apiclient_key.pem文件地址 如C:\Users\Administrator\Desktop\wechat\apiclient_key.pem
|
|||
// */
|
|||
// static String PATH_PEM = "C:\\Users\\dou\\Desktop\\wxpay\\apiclient_key.pem";
|
|||
|
|||
/** |
|||
* 请求地址 |
|||
*/ |
|||
static String NATIVE_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/native"; |
|||
//
|
|||
// /**
|
|||
// * 填写你的返回地址 需要HTTPS返回地址
|
|||
// */
|
|||
// static String RETURN_ADDRESS = "https://test.tall.wiki/gateway/defaultwbs/debug";
|
|||
|
|||
// public static void main(String[] args) {
|
|||
// try {
|
|||
// int money = 100;
|
|||
// String description = "测试支付";
|
|||
// HttpUrl httpurl = HttpUrl.parse(NATIVE_URL);
|
|||
// //签名表头信息 Authorization
|
|||
// String orderId = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
|
|||
// String body = OrderData(orderId, money, description);
|
|||
// String authorization = schema + " " + getToken("POST", httpurl, body, orderId);
|
|||
// //下单调用的接口,JSON格式
|
|||
// String codeUrl = nativePostBody(NATIVE_URL, body, authorization);
|
|||
// System.out.println(codeUrl);
|
|||
//
|
|||
// JSONObject jsonObject = JSONObject.parseObject(codeUrl);
|
|||
// String code_url = jsonObject.getString("code_url");
|
|||
// if(StrUtil.isNotBlank(code_url)){
|
|||
// String s = generateQRCode(code_url);
|
|||
// System.out.println(s);
|
|||
// }
|
|||
// } catch (Exception e) {
|
|||
// e.printStackTrace();
|
|||
// }
|
|||
// }
|
|||
|
|||
// static String OrderData(String orderId, int money, String description) {
|
|||
// // 应用ID appid
|
|||
// // 直连商户号 mchid
|
|||
// // 商品描述 description
|
|||
// //商户订单号 out_trade_no
|
|||
// //通知地址 notify_url
|
|||
// //订单金额 amount对象: 总金额 total 货币类型 currency
|
|||
// JSONObject jsonObject = new JSONObject();
|
|||
// jsonObject.put("mchid", MCH_ID);
|
|||
// jsonObject.put("out_trade_no", orderId);
|
|||
// jsonObject.put("appid", APP_ID);
|
|||
// jsonObject.put("description", description);
|
|||
// jsonObject.put("notify_url", RETURN_ADDRESS);
|
|||
// Map<String, Object> map = new HashMap<>();
|
|||
// map.put("total", money);
|
|||
// map.put("currency", "CNY");
|
|||
// jsonObject.put("amount", map);
|
|||
// return String.valueOf(jsonObject);
|
|||
//
|
|||
// }
|
|||
|
|||
/** |
|||
* 获取签名信息所有值 |
|||
* |
|||
* @param method 请求方法 |
|||
* @param url URL地址 |
|||
* @param body BOdy参数 |
|||
* @return |
|||
*/ |
|||
static String getToken(String method, HttpUrl url, String body, String orderId, String mchId, String serialNo, String pathPem) throws Exception { |
|||
//获得系统时间,把毫秒换算成秒 /1000
|
|||
long timestamp = System.currentTimeMillis() / 1000; |
|||
String message = buildMessage(method, url, timestamp, orderId, body); |
|||
String signature = sign(message.getBytes("utf-8"), pathPem); |
|||
return "mchid=\"" + mchId + "\"," |
|||
+ "nonce_str=\"" + orderId + "\"," |
|||
+ "timestamp=\"" + timestamp + "\"," |
|||
+ "serial_no=\"" + serialNo + "\"," |
|||
+ "signature=\"" + signature + "\""; |
|||
} |
|||
|
|||
/** |
|||
* 拼接明文数值 |
|||
* |
|||
* @param method 请求方法 GET or POST |
|||
* @param url 网络请求方法地址 取除域名项 |
|||
* @param timestamp 时间戳 |
|||
* @param nonceStr 随机数 |
|||
* @param body GET请求不需要Body参数,POST需要Body |
|||
* @return |
|||
*/ |
|||
static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) { |
|||
String canonicalUrl = url.encodedPath(); |
|||
//get请求自动做了校验,会把空字符串进行识别,注意是空字符串!!!!!
|
|||
if (url.encodedQuery() != null) { |
|||
canonicalUrl += "?" + url.encodedQuery(); |
|||
} |
|||
//官方的方法自动做了换行的所有动作,注意唤起支付的参数不一样需要更换(这里是统一下单所以直接照搬即可)
|
|||
return method + "\n" |
|||
+ canonicalUrl + "\n" |
|||
+ timestamp + "\n" |
|||
+ nonceStr + "\n" |
|||
+ body + "\n"; |
|||
} |
|||
|
|||
/** |
|||
* 签名加密 |
|||
* |
|||
* @param message |
|||
* @return |
|||
* @throws NoSuchAlgorithmException |
|||
* @throws SignatureException |
|||
* @throws IOException |
|||
* @throws InvalidKeyException |
|||
*/ |
|||
static String sign(byte[] message, String pathPem) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException { |
|||
//加密方式
|
|||
Signature sign = Signature.getInstance("SHA256withRSA"); |
|||
//私钥,通过getPrivateKey来获取,这是个方法可以接调用 ,需要的是_key.pem文件的绝对路径配上文件名
|
|||
sign.initSign(getPrivateKey(pathPem)); |
|||
sign.update(message); |
|||
|
|||
return Base64.getEncoder().encodeToString(sign.sign()); |
|||
} |
|||
|
|||
/** |
|||
* 获取私钥。 |
|||
* |
|||
* @param filename 私钥文件路径 (required) |
|||
* @return 私钥对象 |
|||
* <p> |
|||
* 完全不需要修改,注意此方法也是去掉了头部和尾部,注意文件路径名 |
|||
*/ |
|||
public static 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("无效的密钥格式"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Post请求带Body参数 |
|||
* |
|||
* @param actionUrl |
|||
* @param params |
|||
* @param requestString |
|||
* @return |
|||
* @throws IOException |
|||
*/ |
|||
public static String nativePostBody(String actionUrl, String params, String requestString) |
|||
throws IOException { |
|||
String serverURL = actionUrl; |
|||
StringBuffer sbf = new StringBuffer(); |
|||
String strRead = null; |
|||
URL url = new URL(serverURL); |
|||
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); |
|||
//请求post方式
|
|||
connection.setRequestMethod("POST"); |
|||
connection.setDoInput(true); |
|||
connection.setDoOutput(true); |
|||
//header内的的参数在这里set
|
|||
connection.setRequestProperty("Content-Type", "application/json"); |
|||
//Native支付需要的参数表头参数
|
|||
connection.setRequestProperty("Authorization", requestString); |
|||
connection.connect(); |
|||
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), "UTF-8"); |
|||
//body参数放这里
|
|||
writer.write(params); |
|||
writer.flush(); |
|||
InputStream is = connection.getInputStream(); |
|||
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); |
|||
while ((strRead = reader.readLine()) != null) { |
|||
sbf.append(strRead); |
|||
sbf.append("\r\n"); |
|||
} |
|||
reader.close(); |
|||
connection.disconnect(); |
|||
String results = sbf.toString(); |
|||
return results; |
|||
} |
|||
|
|||
public static String generateQRCode(String code_url) throws WriterException, IOException { |
|||
//生成二维码
|
|||
//设置二维码的尺寸
|
|||
int width = 200; |
|||
int hight = 200; |
|||
//创建map
|
|||
Map<EncodeHintType, Object> hints = new HashMap<>(); |
|||
hints.put(EncodeHintType.CHARACTER_SET,"UTF-8"); |
|||
|
|||
//创建矩阵对象 调用谷歌提供的
|
|||
BitMatrix bitMatrix = new MultiFormatWriter().encode(code_url, BarcodeFormat.QR_CODE, width, hight, hints); |
|||
|
|||
//创建二维码生成路径
|
|||
String filePath = "C:\\Users\\dou\\Desktop\\wxpay\\"; |
|||
String fileName = RandomStringUtils.randomAlphanumeric(10)+ ".jpg"; |
|||
|
|||
Path path = FileSystems.getDefault().getPath(filePath,fileName); |
|||
|
|||
//将创建的矩阵转换成图片
|
|||
MatrixToImageWriter.writeToPath(bitMatrix,"jpg",path); |
|||
return filePath + fileName; |
|||
} |
|||
} |
Loading…
Reference in new issue