mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2025-04-05 17:38:05 +08:00
This commit is contained in:
parent
609b38a9db
commit
058ce62a2b
@ -23,7 +23,11 @@ public class JedisWxRedisOps implements WxRedisOps {
|
||||
@Override
|
||||
public void setValue(String key, String value, int expire, TimeUnit timeUnit) {
|
||||
try (Jedis jedis = this.jedisPool.getResource()) {
|
||||
jedis.psetex(key, timeUnit.toMillis(expire), value);
|
||||
if (expire <= 0) {
|
||||
jedis.set(key, value);
|
||||
} else {
|
||||
jedis.psetex(key, timeUnit.toMillis(expire), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,11 @@ public class RedisTemplateWxRedisOps implements WxRedisOps {
|
||||
|
||||
@Override
|
||||
public void setValue(String key, String value, int expire, TimeUnit timeUnit) {
|
||||
redisTemplate.opsForValue().set(key, value, expire, timeUnit);
|
||||
if (expire <= 0) {
|
||||
redisTemplate.opsForValue().set(key, value);
|
||||
} else {
|
||||
redisTemplate.opsForValue().set(key, value, expire, timeUnit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,12 +19,20 @@ public class RedissonWxRedisOps implements WxRedisOps {
|
||||
|
||||
@Override
|
||||
public void setValue(String key, String value, int expire, TimeUnit timeUnit) {
|
||||
redissonClient.getBucket(key).set(value, expire, timeUnit);
|
||||
if (expire <= 0) {
|
||||
redissonClient.getBucket(key).set(value);
|
||||
} else {
|
||||
redissonClient.getBucket(key).set(value, expire, timeUnit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getExpire(String key) {
|
||||
return redissonClient.getBucket(key).remainTimeToLive();
|
||||
long expire = redissonClient.getBucket(key).remainTimeToLive();
|
||||
if (expire > 0) {
|
||||
expire = expire / 1000;
|
||||
}
|
||||
return expire;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,51 @@
|
||||
package me.chanjar.weixin.common.redis;
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CommonWxRedisOpsTest {
|
||||
|
||||
protected WxRedisOps wxRedisOps;
|
||||
private String key = "access_token";
|
||||
private String value = String.valueOf(System.currentTimeMillis());
|
||||
|
||||
@Test
|
||||
public void testGetValue() {
|
||||
wxRedisOps.setValue(key, value, 3, TimeUnit.SECONDS);
|
||||
Assert.assertEquals(wxRedisOps.getValue(key), value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValue() {
|
||||
String key = "access_token", value = String.valueOf(System.currentTimeMillis());
|
||||
wxRedisOps.setValue(key, value, -1, TimeUnit.SECONDS);
|
||||
wxRedisOps.setValue(key, value, 0, TimeUnit.SECONDS);
|
||||
wxRedisOps.setValue(key, value, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetExpire() {
|
||||
String key = "access_token", value = String.valueOf(System.currentTimeMillis());
|
||||
wxRedisOps.setValue(key, value, -1, TimeUnit.SECONDS);
|
||||
Assert.assertTrue(wxRedisOps.getExpire(key) < 0);
|
||||
wxRedisOps.setValue(key, value, 4, TimeUnit.SECONDS);
|
||||
Long expireSeconds = wxRedisOps.getExpire(key);
|
||||
Assert.assertTrue(expireSeconds <= 4 && expireSeconds >= 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpire() {
|
||||
String key = "access_token", value = String.valueOf(System.currentTimeMillis());
|
||||
wxRedisOps.setValue(key, value, -1, TimeUnit.SECONDS);
|
||||
wxRedisOps.expire(key, 4, TimeUnit.SECONDS);
|
||||
Long expireSeconds = wxRedisOps.getExpire(key);
|
||||
Assert.assertTrue(expireSeconds <= 4 && expireSeconds >= 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLock() {
|
||||
Assert.assertNotNull(wxRedisOps.getLock("access_token_lock"));
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package me.chanjar.weixin.common.redis;
|
||||
|
||||
import org.testng.annotations.AfterTest;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
|
||||
public class JedisWxRedisOpsTest extends CommonWxRedisOpsTest {
|
||||
|
||||
JedisPool jedisPool;
|
||||
|
||||
@BeforeTest
|
||||
public void init() {
|
||||
this.jedisPool = new JedisPool("127.0.0.1", 6379);
|
||||
this.wxRedisOps = new JedisWxRedisOps(jedisPool);
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
public void destroy() {
|
||||
this.jedisPool.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package me.chanjar.weixin.common.redis;
|
||||
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.testng.annotations.AfterTest;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
|
||||
public class RedisTemplateWxRedisOpsTest extends CommonWxRedisOpsTest {
|
||||
|
||||
StringRedisTemplate redisTemplate;
|
||||
|
||||
@BeforeTest
|
||||
public void init() {
|
||||
JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
|
||||
connectionFactory.setHostName("127.0.0.1");
|
||||
connectionFactory.setPort(6379);
|
||||
connectionFactory.afterPropertiesSet();
|
||||
StringRedisTemplate redisTemplate = new StringRedisTemplate(connectionFactory);
|
||||
this.redisTemplate = redisTemplate;
|
||||
this.wxRedisOps = new RedisTemplateWxRedisOps(this.redisTemplate);
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
public void destroy() {
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package me.chanjar.weixin.common.redis;
|
||||
|
||||
import org.redisson.Redisson;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.config.Config;
|
||||
import org.redisson.config.TransportMode;
|
||||
import org.testng.annotations.AfterTest;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
|
||||
public class RedissonWxRedisOpsTest extends CommonWxRedisOpsTest {
|
||||
|
||||
RedissonClient redissonClient;
|
||||
|
||||
@BeforeTest
|
||||
public void init() {
|
||||
Config config = new Config();
|
||||
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
|
||||
config.setTransportMode(TransportMode.NIO);
|
||||
this.redissonClient = Redisson.create(config);
|
||||
this.wxRedisOps = new RedissonWxRedisOps(this.redissonClient);
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
public void destroy() {
|
||||
this.redissonClient.shutdown();
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package me.chanjar.weixin.mp.api;
|
||||
|
||||
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.mp.bean.invoice.merchant.*;
|
||||
|
||||
/**
|
||||
* 商户电子发票相关的接口
|
||||
* <p>
|
||||
* 重要!!!, 根据不同开票平台, 以下错误码可能开票成功(开票,冲红), 内部暂时未处理:
|
||||
* 73105: 开票平台开票中,请使用相同的发票请求流水号重试开票
|
||||
* 73107: 发票请求流水正在被处理,请通过查询接口获取结果
|
||||
* 73100: 开票平台错误
|
||||
* <p>
|
||||
* 流程文档: https://developers.weixin.qq.com/doc/offiaccount/WeChat_Invoice/E_Invoice/Vendor_and_Invoicing_Platform_Mode_Instruction.html
|
||||
* 接口文档: https://developers.weixin.qq.com/doc/offiaccount/WeChat_Invoice/E_Invoice/Vendor_API_List.html
|
||||
*/
|
||||
public interface WxMpMerchantInvoiceService {
|
||||
|
||||
/**
|
||||
* 获取开票授权页链接
|
||||
*/
|
||||
InvoiceAuthPageResult getAuthPageUrl(InvoiceAuthPageRequest params) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获得用户授权数据
|
||||
*/
|
||||
InvoiceAuthDataResult getAuthData(InvoiceAuthDataRequest params) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 拒绝开票
|
||||
* <p>
|
||||
* 场景: 用户授权填写数据无效
|
||||
* 结果: 用户会收到一条开票失败提示
|
||||
*/
|
||||
void rejectInvoice(InvoiceRejectRequest params) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 开具电子发票
|
||||
*/
|
||||
void makeOutInvoice(MakeOutInvoiceRequest params) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 发票冲红
|
||||
*/
|
||||
void clearOutInvoice(ClearOutInvoiceRequest params) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 查询发票信息
|
||||
*
|
||||
* @param fpqqlsh 发票请求流水号
|
||||
* @param nsrsbh 纳税人识别号
|
||||
*/
|
||||
InvoiceResult queryInvoiceInfo(String fpqqlsh, String nsrsbh) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 设置商户联系方式, 获取授权链接前需要设置商户联系信息
|
||||
*/
|
||||
void setMerchantContactInfo(MerchantContactInfo contact) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获取商户联系方式
|
||||
*/
|
||||
MerchantContactInfo getMerchantContactInfo() throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 配置授权页面字段
|
||||
*/
|
||||
void setAuthPageSetting(InvoiceAuthPageSetting authPageSetting) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获取授权页面配置
|
||||
*/
|
||||
InvoiceAuthPageSetting getAuthPageSetting() throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 设置商户开票平台信息
|
||||
*/
|
||||
void setMerchantInvoicePlatform(MerchantInvoicePlatformInfo merchantInvoicePlatformInfo) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获取商户开票平台信息
|
||||
*/
|
||||
MerchantInvoicePlatformInfo getMerchantInvoicePlatform(MerchantInvoicePlatformInfo merchantInvoicePlatformInfo) throws WxErrorException;
|
||||
}
|
@ -6,10 +6,13 @@ import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.WxType;
|
||||
import me.chanjar.weixin.common.bean.WxJsapiSignature;
|
||||
import me.chanjar.weixin.common.bean.WxNetCheckResult;
|
||||
import me.chanjar.weixin.common.enums.TicketType;
|
||||
import me.chanjar.weixin.common.error.WxError;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.session.StandardSessionManager;
|
||||
@ -26,7 +29,6 @@ import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
|
||||
import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult;
|
||||
import me.chanjar.weixin.mp.bean.result.WxMpUser;
|
||||
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
|
||||
import me.chanjar.weixin.common.enums.TicketType;
|
||||
import me.chanjar.weixin.mp.enums.WxMpApiUrl;
|
||||
import me.chanjar.weixin.mp.util.WxMpConfigStorageHolder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -70,6 +72,10 @@ public abstract class BaseWxMpServiceImpl<H, P> implements WxMpService, RequestH
|
||||
private WxMpOcrService ocrService = new WxMpOcrServiceImpl(this);
|
||||
private WxMpImgProcService imgProcService = new WxMpImgProcServiceImpl(this);
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private WxMpMerchantInvoiceService merchantInvoiceService = new WxMpMerchantInvoiceServiceImpl(this, this.cardService);
|
||||
|
||||
private Map<String, WxMpConfigStorage> configStorageMap;
|
||||
|
||||
private int retrySleepMillis = 1000;
|
||||
@ -359,11 +365,11 @@ public abstract class BaseWxMpServiceImpl<H, P> implements WxMpService, RequestH
|
||||
// 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token
|
||||
Lock lock = this.getWxMpConfigStorage().getAccessTokenLock();
|
||||
lock.lock();
|
||||
try{
|
||||
if(StringUtils.equals(this.getWxMpConfigStorage().getAccessToken(), accessToken)){
|
||||
try {
|
||||
if (StringUtils.equals(this.getWxMpConfigStorage().getAccessToken(), accessToken)) {
|
||||
this.getWxMpConfigStorage().expireAccessToken();
|
||||
}
|
||||
} catch (Exception ex){
|
||||
} catch (Exception ex) {
|
||||
this.getWxMpConfigStorage().expireAccessToken();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
|
@ -0,0 +1,119 @@
|
||||
package me.chanjar.weixin.mp.api.impl;
|
||||
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import lombok.AllArgsConstructor;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.mp.api.WxMpCardService;
|
||||
import me.chanjar.weixin.mp.api.WxMpMerchantInvoiceService;
|
||||
import me.chanjar.weixin.mp.api.WxMpService;
|
||||
import me.chanjar.weixin.mp.bean.invoice.merchant.*;
|
||||
import me.chanjar.weixin.mp.enums.WxMpApiUrl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static me.chanjar.weixin.mp.enums.WxMpApiUrl.Invoice.*;
|
||||
|
||||
|
||||
@AllArgsConstructor
|
||||
public class WxMpMerchantInvoiceServiceImpl implements WxMpMerchantInvoiceService {
|
||||
|
||||
private WxMpService wxMpService;
|
||||
private WxMpCardService wxMpCardService;
|
||||
|
||||
private final static Gson gson;
|
||||
|
||||
static {
|
||||
gson = new GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvoiceAuthPageResult getAuthPageUrl(InvoiceAuthPageRequest params) throws WxErrorException {
|
||||
String ticket = wxMpCardService.getCardApiTicket();
|
||||
params.setTicket(ticket);
|
||||
return doCommonInvoiceHttpPost(GET_AUTH_URL, params, InvoiceAuthPageResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvoiceAuthDataResult getAuthData(InvoiceAuthDataRequest params) throws WxErrorException {
|
||||
return doCommonInvoiceHttpPost(GET_AUTH_DATA, params, InvoiceAuthDataResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rejectInvoice(InvoiceRejectRequest params) throws WxErrorException {
|
||||
doCommonInvoiceHttpPost(REJECT_INSERT, params, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeOutInvoice(MakeOutInvoiceRequest params) throws WxErrorException {
|
||||
doCommonInvoiceHttpPost(MAKE_OUT_INVOICE, params, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearOutInvoice(ClearOutInvoiceRequest params) throws WxErrorException {
|
||||
doCommonInvoiceHttpPost(CLEAR_OUT_INVOICE, params, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvoiceResult queryInvoiceInfo(String fpqqlsh, String nsrsbh) throws WxErrorException {
|
||||
Map data = new HashMap();
|
||||
data.put("fpqqlsh", fpqqlsh);
|
||||
data.put("nsrsbh", nsrsbh);
|
||||
return doCommonInvoiceHttpPost(QUERY_INVOICE_INFO, data, InvoiceResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMerchantContactInfo(MerchantContactInfo contact) throws WxErrorException {
|
||||
MerchantContactInfoWrapper data = new MerchantContactInfoWrapper();
|
||||
data.setContact(contact);
|
||||
doCommonInvoiceHttpPost(SET_CONTACT_SET_BIZ_ATTR, data, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MerchantContactInfo getMerchantContactInfo() throws WxErrorException {
|
||||
MerchantContactInfoWrapper merchantContactInfoWrapper = doCommonInvoiceHttpPost(GET_CONTACT_SET_BIZ_ATTR, null, MerchantContactInfoWrapper.class);
|
||||
return merchantContactInfoWrapper == null ? null : merchantContactInfoWrapper.getContact();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthPageSetting(InvoiceAuthPageSetting authPageSetting) throws WxErrorException {
|
||||
doCommonInvoiceHttpPost(SET_AUTH_FIELD_SET_BIZ_ATTR, authPageSetting, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvoiceAuthPageSetting getAuthPageSetting() throws WxErrorException {
|
||||
return doCommonInvoiceHttpPost(GET_AUTH_FIELD_SET_BIZ_ATTR, new JsonObject(), InvoiceAuthPageSetting.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMerchantInvoicePlatform(MerchantInvoicePlatformInfo paymchInfo) throws WxErrorException {
|
||||
MerchantInvoicePlatformInfoWrapper data = new MerchantInvoicePlatformInfoWrapper();
|
||||
data.setPaymchInfo(paymchInfo);
|
||||
doCommonInvoiceHttpPost(SET_PAY_MCH_SET_BIZ_ATTR, data, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MerchantInvoicePlatformInfo getMerchantInvoicePlatform(MerchantInvoicePlatformInfo merchantInvoicePlatformInfo) throws WxErrorException {
|
||||
MerchantInvoicePlatformInfoWrapper result = doCommonInvoiceHttpPost(GET_PAY_MCH_SET_BIZ_ATTR, new JsonObject(), MerchantInvoicePlatformInfoWrapper.class);
|
||||
return result == null ? null : result.getPaymchInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 电子发票公用post请求方法
|
||||
*/
|
||||
private <T> T doCommonInvoiceHttpPost(WxMpApiUrl url, Object data, Class<T> resultClass) throws WxErrorException {
|
||||
String json = "";
|
||||
if (data != null) {
|
||||
json = gson.toJson(data);
|
||||
}
|
||||
String responseText = wxMpService.post(url, json);
|
||||
if (resultClass == null) return null;
|
||||
return gson.fromJson(responseText, resultClass);
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package me.chanjar.weixin.mp.bean.invoice.merchant;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 发票充红请求参数
|
||||
*/
|
||||
@Data
|
||||
public class ClearOutInvoiceRequest implements Serializable {
|
||||
|
||||
|
||||
private ClearOutInvoiceInfo invoiceinfo;
|
||||
|
||||
@Data
|
||||
public static class ClearOutInvoiceInfo implements Serializable {
|
||||
|
||||
/**
|
||||
* 用户的openid 用户知道是谁在开票
|
||||
*/
|
||||
private String wxopenid;
|
||||
|
||||
/**
|
||||
* 发票请求流水号,唯一查询发票的流水号
|
||||
*/
|
||||
private String fpqqlsh;
|
||||
|
||||
/**
|
||||
* 纳税人识别码
|
||||
*/
|
||||
private String nsrsbh;
|
||||
|
||||
/**
|
||||
* 纳税人名称
|
||||
*/
|
||||
private String nsrmc;
|
||||
|
||||
/**
|
||||
* 原发票代码,即要冲红的蓝票的发票代码
|
||||
*/
|
||||
private String yfpdm;
|
||||
|
||||
/**
|
||||
* 原发票号码,即要冲红的蓝票的发票号码
|
||||
*/
|
||||
private String yfphm;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package me.chanjar.weixin.mp.bean.invoice.merchant;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 获取电子开票用户授权数据
|
||||
*/
|
||||
@Data
|
||||
public class InvoiceAuthDataRequest implements Serializable {
|
||||
|
||||
/**
|
||||
* 开票平台在微信的标识号,商户需要找开票平台提供
|
||||
*/
|
||||
private String sPappid;
|
||||
|
||||
/**
|
||||
* 订单id,在商户内单笔开票请求的唯一识别号
|
||||
*/
|
||||
private String orderId;
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package me.chanjar.weixin.mp.bean.invoice.merchant;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户开票认证信息返回结果DTO
|
||||
*/
|
||||
@Data
|
||||
public class InvoiceAuthDataResult implements Serializable {
|
||||
|
||||
/**
|
||||
* 订单授权状态,当errcode为0时会出现
|
||||
*/
|
||||
private String invoiceStatus;
|
||||
|
||||
/**
|
||||
* 授权时间,为十位时间戳(utc+8),当errcode为0时会出现
|
||||
*/
|
||||
private Long authTime;
|
||||
|
||||
/**
|
||||
* 用户授权信息
|
||||
*/
|
||||
private UserAuthInfo userAuthInfo;
|
||||
|
||||
@Data
|
||||
public static class UserAuthInfo implements Serializable {
|
||||
/**
|
||||
* 个人抬头
|
||||
*/
|
||||
private UserField userField;
|
||||
|
||||
/**
|
||||
* 单位抬头
|
||||
*/
|
||||
private BizField bizField;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class UserField implements Serializable {
|
||||
private String title;
|
||||
private String phone;
|
||||
private String email;
|
||||
private List<KeyValuePair> customField;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class BizField implements Serializable {
|
||||
private String title;
|
||||
private String taxNo;
|
||||
private String addr;
|
||||
private String phone;
|
||||
private String bankType;
|
||||
private String bankNo;
|
||||
private List<KeyValuePair> customField;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class KeyValuePair implements Serializable {
|
||||
private String key;
|
||||
private String value;
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package me.chanjar.weixin.mp.bean.invoice.merchant;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 获取授权地址的输入参数
|
||||
*/
|
||||
@Data
|
||||
public class InvoiceAuthPageRequest implements Serializable {
|
||||
|
||||
/**
|
||||
* 开票平台在微信的标识号,商户需要找开票平台提供
|
||||
*/
|
||||
private String sPappid;
|
||||
|
||||
/**
|
||||
* 订单id,在商户内单笔开票请求的唯一识别号
|
||||
*/
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* 订单金额,以分为单位
|
||||
*/
|
||||
private Long money;
|
||||
|
||||
/**
|
||||
* 开票来源
|
||||
*/
|
||||
private String source;
|
||||
|
||||
/**
|
||||
* 授权成功后跳转页面。本字段只有在source为H5的时候需要填写,引导用户在微信中进行下一步流程。app开票因为从外部app拉起微信授权页,授权完成后自动回到原来的app,故无需填写。
|
||||
*/
|
||||
private String redirectUrl;
|
||||
|
||||
/**
|
||||
* 授权类型,0:开票授权,1:填写字段开票授权,2:领票授权
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 时间戳单位s
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 内部填充(请务设置)
|
||||
*/
|
||||
private String ticket;
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package me.chanjar.weixin.mp.bean.invoice.merchant;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 获取授权链接返回结果DTO
|
||||
*/
|
||||
@Data
|
||||
public class InvoiceAuthPageResult implements Serializable {
|
||||
|
||||
/**
|
||||
* 授权页地址
|
||||
*/
|
||||
private String authUrl;
|
||||
|
||||
/**
|
||||
* 当发起端为小程序时, 返回
|
||||
*/
|
||||
private String appid;
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package me.chanjar.weixin.mp.bean.invoice.merchant;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class InvoiceAuthPageSetting implements Serializable {
|
||||
|
||||
private AuthField authField;
|
||||
|
||||
@Data
|
||||
public static class AuthField implements Serializable {
|
||||
private UserField userField;
|
||||
private BizField bizField;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class UserField implements Serializable {
|
||||
private Integer showTitle;
|
||||
private Integer showPhone;
|
||||
private Integer showEmail;
|
||||
private Integer requirePhone;
|
||||
private Integer requireEmail;
|
||||
private List<InvoiceAuthDataResult.KeyValuePair> customField;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class BizField implements Serializable {
|
||||
private Integer showTitle;
|
||||
private Integer showTaxNo;
|
||||
private Integer showAddr;
|
||||
private Integer showPhone;
|
||||
private Integer showBankType;
|
||||
private Integer showBankNo;
|
||||
|
||||
private Integer requireTaxNo;
|
||||
private Integer requireAddr;
|
||||
private Integer requirePhone;
|
||||
private Integer requireBankType;
|
||||
private Integer requireBankNo;
|
||||
private List<InvoiceAuthDataResult.KeyValuePair> customField;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class CustomField implements Serializable {
|
||||
/**
|
||||
* 字段名
|
||||
*/
|
||||
private String key;
|
||||
/**
|
||||
* 0:否,1:是, 默认为0
|
||||
*/
|
||||
private Integer isRequire;
|
||||
/**
|
||||
* 提示文案
|
||||
*/
|
||||
private String notice;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package me.chanjar.weixin.mp.bean.invoice.merchant;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 拒绝开票请求参数
|
||||
*/
|
||||
public class InvoiceRejectRequest implements Serializable {
|
||||
|
||||
/**
|
||||
* 开票平台标示
|
||||
*/
|
||||
private String sPappid;
|
||||
|
||||
/**
|
||||
* 订单id
|
||||
*/
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* 拒绝原因
|
||||
*/
|
||||
private String reason;
|
||||
|
||||
/**
|
||||
* 引导用户跳转url
|
||||
*/
|
||||
private String url;
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package me.chanjar.weixin.mp.bean.invoice.merchant;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 电子发票信息查询结果
|
||||
*/
|
||||
@Data
|
||||
public class InvoiceResult implements Serializable {
|
||||
|
||||
/**
|
||||
* 发票相关信息
|
||||
*/
|
||||
private InvoiceDetail invoicedetail;
|
||||
|
||||
@Data
|
||||
public static class InvoiceDetail implements Serializable {
|
||||
/**
|
||||
* 发票流水号
|
||||
*/
|
||||
private String fpqqlsh;
|
||||
|
||||
/**
|
||||
* 检验码
|
||||
*/
|
||||
private String jym;
|
||||
|
||||
/**
|
||||
* 校验码
|
||||
*/
|
||||
private String kprq;
|
||||
|
||||
/**
|
||||
* 发票代码
|
||||
*/
|
||||
private String fpdm;
|
||||
|
||||
/**
|
||||
* 发票号码
|
||||
*/
|
||||
private String fphm;
|
||||
|
||||
/**
|
||||
* 发票url
|
||||
*/
|
||||
private String pdfurl;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,200 @@
|
||||
package me.chanjar.weixin.mp.bean.invoice.merchant;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 开票信息请求参数
|
||||
*/
|
||||
@Data
|
||||
public class MakeOutInvoiceRequest implements Serializable {
|
||||
|
||||
private InvoiceInfo invoiceinfo;
|
||||
|
||||
/**
|
||||
* 发票信息
|
||||
*/
|
||||
@Data
|
||||
public static class InvoiceInfo implements Serializable {
|
||||
/**
|
||||
* 维修openid
|
||||
*/
|
||||
private String wxopenid;
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String ddh;
|
||||
|
||||
/**
|
||||
* 发票请求流水号,唯一识别开票请求的流水号
|
||||
*/
|
||||
private String fpqqlsh;
|
||||
|
||||
/**
|
||||
* 纳税人识别码
|
||||
*/
|
||||
private String nsrsbh;
|
||||
|
||||
/**
|
||||
* 纳税人名称
|
||||
*/
|
||||
private String nsrmc;
|
||||
|
||||
/**
|
||||
* 纳税人地址
|
||||
*/
|
||||
private String nsrdz;
|
||||
|
||||
/**
|
||||
* 纳税人电话
|
||||
*/
|
||||
private String nsrdh;
|
||||
|
||||
/**
|
||||
* 纳税人开户行
|
||||
*/
|
||||
private String nsrbank;
|
||||
|
||||
/**
|
||||
* 纳税人银行账号
|
||||
*/
|
||||
private String nsrbankid;
|
||||
|
||||
/**
|
||||
* 购货方名称
|
||||
*/
|
||||
private String ghfnsrsbh;
|
||||
|
||||
/**
|
||||
* 购货方识别号
|
||||
*/
|
||||
private String ghfmc;
|
||||
|
||||
/**
|
||||
* 购货方地址
|
||||
*/
|
||||
private String ghfdz;
|
||||
|
||||
/**
|
||||
* 购货方电话
|
||||
*/
|
||||
private String ghfdh;
|
||||
|
||||
/**
|
||||
* 购货方开户行
|
||||
*/
|
||||
private String ghfbank;
|
||||
|
||||
/**
|
||||
* 购货方银行帐号
|
||||
*/
|
||||
private String ghfbankid;
|
||||
|
||||
/**
|
||||
* 开票人
|
||||
*/
|
||||
private String kpr;
|
||||
|
||||
/**
|
||||
* 收款人
|
||||
*/
|
||||
private String skr;
|
||||
|
||||
/**
|
||||
* 复核人
|
||||
*/
|
||||
private String fhr;
|
||||
|
||||
/**
|
||||
* 价税合计
|
||||
*/
|
||||
private String jshj;
|
||||
|
||||
/**
|
||||
* 合计金额
|
||||
*/
|
||||
private String hjje;
|
||||
|
||||
/**
|
||||
* 合计税额
|
||||
*/
|
||||
private String hjse;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String bz;
|
||||
|
||||
/**
|
||||
* 行业类型 0 商业 1其它
|
||||
*/
|
||||
private String hylx;
|
||||
|
||||
/**
|
||||
* 发票商品条目
|
||||
*/
|
||||
private List<InvoiceDetailItem> invoicedetailList;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 发票条目
|
||||
*/
|
||||
@Data
|
||||
public static class InvoiceDetailItem implements Serializable {
|
||||
/**
|
||||
* 发票性质
|
||||
*/
|
||||
private String fphxz;
|
||||
|
||||
/**
|
||||
* 19位税收分类编码
|
||||
*/
|
||||
private String spbm;
|
||||
|
||||
/**
|
||||
* 项目名称
|
||||
*/
|
||||
private String xmmc;
|
||||
|
||||
/**
|
||||
* 计量单位
|
||||
*/
|
||||
private String dw;
|
||||
|
||||
/**
|
||||
* 规格型号
|
||||
*/
|
||||
private String ggxh;
|
||||
|
||||
/**
|
||||
* 项目数量
|
||||
*/
|
||||
private String xmsl;
|
||||
|
||||
/**
|
||||
* 项目单价
|
||||
*/
|
||||
private String xmdj;
|
||||
|
||||
/**
|
||||
* 项目金额 不含税,单位元 两位小数
|
||||
*/
|
||||
private String xmje;
|
||||
|
||||
/**
|
||||
* 税率 精确到两位小数 如0.01
|
||||
*/
|
||||
private String sl;
|
||||
|
||||
/**
|
||||
* 税额 单位元 两位小数
|
||||
*/
|
||||
private String se;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package me.chanjar.weixin.mp.bean.invoice.merchant;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 商户联系信息
|
||||
*/
|
||||
@Data
|
||||
public class MerchantContactInfo implements Serializable {
|
||||
/**
|
||||
* 联系电话
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 开票超时时间
|
||||
*/
|
||||
private Integer timeout;
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package me.chanjar.weixin.mp.bean.invoice.merchant;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 设置商户联系信息和发票过时时间参数
|
||||
*/
|
||||
@Data
|
||||
public class MerchantContactInfoWrapper implements Serializable {
|
||||
|
||||
private MerchantContactInfo contact;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package me.chanjar.weixin.mp.bean.invoice.merchant;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 商户的开票平台信息
|
||||
*/
|
||||
public class MerchantInvoicePlatformInfo implements Serializable {
|
||||
|
||||
/**
|
||||
* 微信支付商户号
|
||||
*/
|
||||
private String mchid;
|
||||
|
||||
/**
|
||||
* 为该商户提供开票服务的开票平台 id ,由开票平台提供给商户
|
||||
*/
|
||||
private String sPappid;
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package me.chanjar.weixin.mp.bean.invoice.merchant;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 设置商户联系信息和发票过时时间参数
|
||||
*/
|
||||
@Data
|
||||
public class MerchantInvoicePlatformInfoWrapper implements Serializable {
|
||||
|
||||
private MerchantInvoicePlatformInfo paymchInfo;
|
||||
|
||||
}
|
@ -1082,4 +1082,78 @@ public interface WxMpApiUrl {
|
||||
}
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
enum Invoice implements WxMpApiUrl {
|
||||
|
||||
/**
|
||||
* 获取用户开票授权地址
|
||||
*/
|
||||
GET_AUTH_URL(API_DEFAULT_HOST_URL, "/card/invoice/getauthurl"),
|
||||
|
||||
/**
|
||||
* 获取用户开票授权信息
|
||||
*/
|
||||
GET_AUTH_DATA(API_DEFAULT_HOST_URL, "/card/invoice/getauthdata"),
|
||||
|
||||
/**
|
||||
* 拒绝为用户开票
|
||||
*/
|
||||
REJECT_INSERT(API_DEFAULT_HOST_URL, "/card/invoice/rejectinsert"),
|
||||
|
||||
/**
|
||||
* 开票
|
||||
*/
|
||||
MAKE_OUT_INVOICE(API_DEFAULT_HOST_URL, "/card/invoice/makeoutinvoice"),
|
||||
|
||||
/**
|
||||
* 发票冲红
|
||||
*/
|
||||
CLEAR_OUT_INVOICE(API_DEFAULT_HOST_URL, "/card/invoice/clearoutinvoice"),
|
||||
|
||||
/**
|
||||
* 查询发票信息
|
||||
*/
|
||||
QUERY_INVOICE_INFO(API_DEFAULT_HOST_URL, "/card/invoice/queryinvoceinfo"),
|
||||
|
||||
/**
|
||||
* 设置商户信息联系
|
||||
*/
|
||||
SET_CONTACT_SET_BIZ_ATTR(API_DEFAULT_HOST_URL, "/card/invoice/setbizattr?action=set_contact"),
|
||||
|
||||
/**
|
||||
* 获取商户联系信息
|
||||
*/
|
||||
GET_CONTACT_SET_BIZ_ATTR(API_DEFAULT_HOST_URL, "/card/invoice/setbizattr?action=get_contact"),
|
||||
|
||||
/**
|
||||
* 设置授权页面字段
|
||||
*/
|
||||
SET_AUTH_FIELD_SET_BIZ_ATTR(API_DEFAULT_HOST_URL, "/card/invoice/setbizattr?action=set_auth_field"),
|
||||
|
||||
/**
|
||||
* 获取授权页面字段
|
||||
*/
|
||||
GET_AUTH_FIELD_SET_BIZ_ATTR(API_DEFAULT_HOST_URL, "/card/invoice/setbizattr?action=get_auth_field"),
|
||||
|
||||
/**
|
||||
* 设置关联商户
|
||||
*/
|
||||
SET_PAY_MCH_SET_BIZ_ATTR(API_DEFAULT_HOST_URL, "/card/invoice/setbizattr?action=set_pay_mch"),
|
||||
|
||||
/**
|
||||
* 获取关联商户
|
||||
*/
|
||||
GET_PAY_MCH_SET_BIZ_ATTR(API_DEFAULT_HOST_URL, "/card/invoice/setbizattr?action=get_pay_mch"),
|
||||
;
|
||||
private String prefix;
|
||||
private String path;
|
||||
|
||||
@Override
|
||||
public String getUrl(WxMpConfigStorage config) {
|
||||
if (null == config) {
|
||||
return buildUrl(null, prefix, path);
|
||||
}
|
||||
return buildUrl(config.getHostConfig(), prefix, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,10 @@ package me.chanjar.weixin.open.api.impl;
|
||||
import cn.binarywang.wx.miniapp.config.WxMaConfig;
|
||||
import lombok.Data;
|
||||
import me.chanjar.weixin.common.bean.WxAccessToken;
|
||||
import me.chanjar.weixin.common.enums.TicketType;
|
||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
|
||||
import me.chanjar.weixin.mp.bean.WxMpHostConfig;
|
||||
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
|
||||
import me.chanjar.weixin.common.enums.TicketType;
|
||||
import me.chanjar.weixin.open.api.WxOpenConfigStorage;
|
||||
import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken;
|
||||
import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken;
|
||||
@ -46,9 +46,6 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
|
||||
private Map<String, Token> cardApiTickets = new ConcurrentHashMap<>();
|
||||
private Map<String, Lock> locks = new ConcurrentHashMap<>();
|
||||
|
||||
private Lock componentAccessTokenLock = getLockByKey("componentAccessTokenLock");
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isComponentAccessTokenExpired() {
|
||||
return System.currentTimeMillis() > componentExpiresTime;
|
||||
@ -64,11 +61,25 @@ public class WxOpenInMemoryConfigStorage implements WxOpenConfigStorage {
|
||||
updateComponentAccessToken(componentAccessToken.getComponentAccessToken(), componentAccessToken.getExpiresIn());
|
||||
}
|
||||
|
||||
private Lock accessTokenLockInstance;
|
||||
|
||||
@Override
|
||||
public Lock getLockByKey(String key){
|
||||
public Lock getComponentAccessTokenLock() {
|
||||
if (this.accessTokenLockInstance == null) {
|
||||
synchronized (this) {
|
||||
if (this.accessTokenLockInstance == null) {
|
||||
this.accessTokenLockInstance = getLockByKey("componentAccessTokenLock");
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.accessTokenLockInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lock getLockByKey(String key) {
|
||||
Lock lock = locks.get(key);
|
||||
if (lock == null) {
|
||||
synchronized (WxOpenInMemoryConfigStorage.class){
|
||||
synchronized (this) {
|
||||
lock = locks.get(key);
|
||||
if (lock == null) {
|
||||
lock = new ReentrantLock();
|
||||
|
Loading…
Reference in New Issue
Block a user