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