From 51fca5a6eb69e4dd5e3b19781d038e52bf6c72c2 Mon Sep 17 00:00:00 2001 From: yeungchihang <842328916@qq.com> Date: Wed, 28 Sep 2022 16:48:11 +0800 Subject: [PATCH] =?UTF-8?q?v1.0=EF=BC=9A=E6=8E=A5=E5=85=A5=E8=B0=B7?= =?UTF-8?q?=E6=AD=8C=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../accompany/common/constant/Constant.java | 22 ++ accompany-base/accompany-payment/pom.xml | 6 +- .../payment/config/GooglePlayConfig.java | 72 +++++++ .../payment/dto/AppInnerPayRecordDTO.java | 13 ++ .../payment/dto/GooglePayLimitConfigDTO.java | 29 +++ .../google/AndroidPublisherHelper.java | 107 ++++++++++ .../google/GooglePlayBillingService.java | 200 ++++++++++++++++++ .../payment/mapper/ChargeRecordMapperMgr.java | 4 + .../payment/service/ChargeRecordService.java | 11 + .../resources/mapper/ChargeRecordMapper.xml | 3 +- .../mapper/ChargeRecordMapperMgr.xml | 15 ++ .../GooglePlayBillingChargeController.java | 56 +++++ .../GooglePlayBillingServiceTest.java | 35 +++ 13 files changed, 571 insertions(+), 2 deletions(-) create mode 100644 accompany-base/accompany-payment/src/main/java/com/accompany/payment/config/GooglePlayConfig.java create mode 100644 accompany-base/accompany-payment/src/main/java/com/accompany/payment/dto/AppInnerPayRecordDTO.java create mode 100644 accompany-base/accompany-payment/src/main/java/com/accompany/payment/dto/GooglePayLimitConfigDTO.java create mode 100644 accompany-base/accompany-payment/src/main/java/com/accompany/payment/google/AndroidPublisherHelper.java create mode 100644 accompany-base/accompany-payment/src/main/java/com/accompany/payment/google/GooglePlayBillingService.java create mode 100644 accompany-business/accompany-business-web/src/main/java/com/accompany/business/controller/apppay/GooglePlayBillingChargeController.java create mode 100644 accompany-business/accompany-business-web/src/test/java/servicetest/GooglePlayBillingServiceTest.java diff --git a/accompany-base/accompany-common/src/main/java/com/accompany/common/constant/Constant.java b/accompany-base/accompany-common/src/main/java/com/accompany/common/constant/Constant.java index c6a182e9a..4a73b09ee 100644 --- a/accompany-base/accompany-common/src/main/java/com/accompany/common/constant/Constant.java +++ b/accompany-base/accompany-common/src/main/java/com/accompany/common/constant/Constant.java @@ -389,6 +389,8 @@ public class Constant { public static final String lucky_tarot = "lucky_tarot"; // 新公众号:平台助手 h5支付 public static final String wx_pub2_h5 = "wx_pub2_h5"; + // google play billing + public static final String google_play_billing = "google_play_billing"; } public static class DepositStatus { @@ -1739,6 +1741,8 @@ public class Constant { /** 魔法学院奖励配置 */ public static final String ACT_MAGIC_SCHOOL_AWARD_CONFIG = "act_magic_school_award_config"; + + public static final String GOOGLE_PAY_LIMIT_CONFIG = "google_pay_limit_config"; } public static class ActiveMq { @@ -5574,5 +5578,23 @@ public class Constant { // 初次高级探险 public static final Integer FIRST_HIGH = 2; } + + public static final class GooglePurchaseState { + /** + * 已支付 + */ + public static final Integer PURCHASED = 0; + + /** + * 取消 + */ + public static final Integer CANCELED = 1; + + /** + * 支付中 + */ + public static final Integer PENDING = 2; + } + } diff --git a/accompany-base/accompany-payment/pom.xml b/accompany-base/accompany-payment/pom.xml index af7fa534c..18be08111 100644 --- a/accompany-base/accompany-payment/pom.xml +++ b/accompany-base/accompany-payment/pom.xml @@ -28,7 +28,11 @@ alipay-sdk-java 3.7.110.ALL + + com.google.apis + google-api-services-androidpublisher + v3-rev24-1.24.1 + - \ No newline at end of file diff --git a/accompany-base/accompany-payment/src/main/java/com/accompany/payment/config/GooglePlayConfig.java b/accompany-base/accompany-payment/src/main/java/com/accompany/payment/config/GooglePlayConfig.java new file mode 100644 index 000000000..6b1c440cb --- /dev/null +++ b/accompany-base/accompany-payment/src/main/java/com/accompany/payment/config/GooglePlayConfig.java @@ -0,0 +1,72 @@ +package com.accompany.payment.config; + +import com.accompany.payment.google.AndroidPublisherHelper; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.nacos.api.exception.NacosException; +import com.google.api.services.androidpublisher.AndroidPublisher; +import lombok.Data; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.cloud.context.environment.EnvironmentChangeEvent; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import org.springframework.context.annotation.Lazy; +import org.springframework.context.event.EventListener; +import org.springframework.core.annotation.Order; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Arrays; +import java.util.List; + +@Configuration +@Order(-1) +@Lazy(false) +@ConfigurationProperties(prefix = "google-play") +@RefreshScope +@Data +public class GooglePlayConfig { + + private String applicationName; + + private String credentialJson; + + private static final String CONFIG_NAME_APPLICATIONNAME = "google-play.applicationName"; + private static final String CONFIG_NAME_JSON = "google-play.credentialJson"; + @Autowired + private ConfigurableApplicationContext applicationContext; + + @Bean("androidPublisher") + public AndroidPublisher initAndroidPublisher() throws IOException, GeneralSecurityException { + return AndroidPublisherHelper.init(applicationName, credentialJson); + } + + /** + * 配置修改监听器,用于当配置修改后,重新生成AndroidPublisher bean + * @param event + */ + @EventListener + @Async + public void handle(EnvironmentChangeEvent event) throws IOException, GeneralSecurityException { + List needReinitConfigFields = Arrays.asList(CONFIG_NAME_APPLICATIONNAME, CONFIG_NAME_JSON); + boolean needReInit = false; + for (String needReinitConfigField : needReinitConfigFields) { + if (event.getKeys().contains(needReinitConfigField)) { + needReInit = true; + break; + } + } + if (needReInit) { + String application = applicationContext.getEnvironment().getProperty(CONFIG_NAME_APPLICATIONNAME); + String json = applicationContext.getEnvironment().getProperty(CONFIG_NAME_JSON); + + AndroidPublisherHelper.init(application, json); + } + } + +} diff --git a/accompany-base/accompany-payment/src/main/java/com/accompany/payment/dto/AppInnerPayRecordDTO.java b/accompany-base/accompany-payment/src/main/java/com/accompany/payment/dto/AppInnerPayRecordDTO.java new file mode 100644 index 000000000..7db118d75 --- /dev/null +++ b/accompany-base/accompany-payment/src/main/java/com/accompany/payment/dto/AppInnerPayRecordDTO.java @@ -0,0 +1,13 @@ +package com.accompany.payment.dto; + +import lombok.Data; + +/** + * Created by 北岭山下 on 2017/7/13. + */ +@Data +public class AppInnerPayRecordDTO { + + private String recordId; + +} diff --git a/accompany-base/accompany-payment/src/main/java/com/accompany/payment/dto/GooglePayLimitConfigDTO.java b/accompany-base/accompany-payment/src/main/java/com/accompany/payment/dto/GooglePayLimitConfigDTO.java new file mode 100644 index 000000000..32c80c243 --- /dev/null +++ b/accompany-base/accompany-payment/src/main/java/com/accompany/payment/dto/GooglePayLimitConfigDTO.java @@ -0,0 +1,29 @@ +package com.accompany.payment.dto; + +import lombok.Data; + +import java.util.List; + +@Data +public class GooglePayLimitConfigDTO { + + /** + * 每日总共限制总额金额 + */ + private Long limitEveryDayAmount; + + /** + * 每日单人总共限制总额金额 + */ + private Long limitEveryOneDaySumAmount; + + /** + * 错误提示 + */ + private String errorTip; + + /** + * google内购项id + */ + private List googleChargeProdIds; +} diff --git a/accompany-base/accompany-payment/src/main/java/com/accompany/payment/google/AndroidPublisherHelper.java b/accompany-base/accompany-payment/src/main/java/com/accompany/payment/google/AndroidPublisherHelper.java new file mode 100644 index 000000000..7f3dbf46d --- /dev/null +++ b/accompany-base/accompany-payment/src/main/java/com/accompany/payment/google/AndroidPublisherHelper.java @@ -0,0 +1,107 @@ +package com.accompany.payment.google; + +import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.client.util.Preconditions; +import com.google.api.client.util.Strings; +import com.google.api.services.androidpublisher.AndroidPublisher; +import com.google.api.services.androidpublisher.AndroidPublisherScopes; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.util.Assert; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.List; + +/** + * Helper class to initialize the publisher APIs client library. + *

+ * Before making any calls to the API through the client library you need to + * call the {@link AndroidPublisherHelper#init(String, String, String, String)} method. This will run + * all precondition checks for for client id and secret setup properly in + * resources/client_secrets.json and authorize this client against the API. + *

+ */ +public class AndroidPublisherHelper { + + private static final Log log = LogFactory.getLog(AndroidPublisherHelper.class); + + /** Global instance of the JSON factory. */ + private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); + + /** Global instance of the HTTP transport. */ + private static HttpTransport HTTP_TRANSPORT; + + private static volatile AndroidPublisher androidPublisher; + + private static volatile String oldApplicationName; + + private static volatile String oldCredentialJson; + + + public static AndroidPublisher init(String applicationName, String json) throws IOException, GeneralSecurityException { + if (needInit(applicationName, json)) { + synchronized (AndroidPublisherHelper.class) { + if (needInit(applicationName, json) || androidPublisher == null) { + log.info("start init AndroidPublisher"); + Preconditions.checkArgument(!Strings.isNullOrEmpty(applicationName), + "applicationName cannot be null or empty!"); + + List scopes = new ArrayList<>(); + scopes.add(AndroidPublisherScopes.ANDROIDPUBLISHER); + ByteArrayResource resource = new ByteArrayResource(json.getBytes()); + GoogleCredential credential = GoogleCredential.fromStream(resource.getInputStream()) + .createScoped(scopes); + + newTrustedTransport(); + + //使用谷歌凭据和收据从谷歌获取购买信息 + androidPublisher = new AndroidPublisher.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential) + .setApplicationName(applicationName) + .build(); + + oldApplicationName = applicationName; + oldCredentialJson = json; + } + } + } + + return androidPublisher; + } + + /** + * 判断是否需要初始化 + * @param applicationName + * @param credentialJsonPath + * @return + */ + private static boolean needInit(String applicationName, String credentialJsonPath) { + boolean firstInit = AndroidPublisherHelper.oldApplicationName == null || AndroidPublisherHelper.oldCredentialJson == null; + + // json配置修改后,重新初始化 + boolean configChanged = StringUtils.isNotBlank(AndroidPublisherHelper.oldCredentialJson) + && !AndroidPublisherHelper.oldCredentialJson.equals(credentialJsonPath); + + return firstInit || configChanged; + } + + private static void newTrustedTransport() throws GeneralSecurityException, + IOException { + if (null == HTTP_TRANSPORT) { + HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport(); + } + } + + public static AndroidPublisher getPublisher() { + Assert.notNull(androidPublisher, "should init before get bean"); + return androidPublisher; + } + +} diff --git a/accompany-base/accompany-payment/src/main/java/com/accompany/payment/google/GooglePlayBillingService.java b/accompany-base/accompany-payment/src/main/java/com/accompany/payment/google/GooglePlayBillingService.java new file mode 100644 index 000000000..fb0b137a0 --- /dev/null +++ b/accompany-base/accompany-payment/src/main/java/com/accompany/payment/google/GooglePlayBillingService.java @@ -0,0 +1,200 @@ +package com.accompany.payment.google; + +import com.accompany.common.constant.Constant; +import com.accompany.common.redis.RedisKey; +import com.accompany.common.status.BusiStatus; +import com.accompany.common.utils.DateTimeUtil; +import com.accompany.common.utils.UUIDUitl; +import com.accompany.core.exception.ServiceException; +import com.accompany.core.model.Users; +import com.accompany.core.service.SysConfService; +import com.accompany.core.service.user.UsersBaseService; +import com.accompany.payment.dto.AppInnerPayRecordDTO; +import com.accompany.payment.dto.GooglePayLimitConfigDTO; +import com.accompany.payment.model.ChargeProd; +import com.accompany.payment.model.ChargeRecord; +import com.accompany.payment.service.ChargeProdService; +import com.accompany.payment.service.ChargeRecordService; +import com.alibaba.fastjson.JSONObject; +import com.google.api.services.androidpublisher.model.ProductPurchase; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * google 内购 + */ +@Service +@Slf4j +public class GooglePlayBillingService { + + @Autowired + private RedissonClient redissonClient; + @Autowired + private ChargeRecordService chargeRecordService; + @Autowired + private ChargeProdService chargeProdService; + @Autowired + private SysConfService sysConfService; + @Autowired + private UsersBaseService usersBaseService; + + protected String getPayChannel() { + return Constant.ChargeChannel.google_play_billing; + } + + public AppInnerPayRecordDTO placeOrder(Long uid, String chargeProdId, String clientIp, String deviceId) { + validMoneyLimit(uid, chargeProdId); + + RLock lock = redissonClient.getLock(RedisKey.lock_apply_charge.getKey(uid.toString())); + try { + lock.tryLock(10, TimeUnit.SECONDS); + + ChargeProd chargeProd = chargeProdService.getChargeProdById(chargeProdId); + if (chargeProd == null) { + log.error("充值产品不存在,prodId: {} ", chargeProdId); + throw new ServiceException(BusiStatus.CHARGE_PROD_NOT_EXIST); + } + + String payChannel = getPayChannel(); + log.info("用户 {} 内购充值,渠道: {}", uid, payChannel); + //保存充值记录 + //1.创建订单号 + //UUID不会重复,所以不需要判断是否生成重复的订单号 + String chargeRecordId = UUIDUitl.get(); + + Users users = usersBaseService.getUsersByUid(uid); + //Integer region = users.getRegion() == null ? Constant.region.overseas : users.getRegion(); + + ChargeRecord chargeRecord = new ChargeRecord(); + chargeRecord.setChargeRecordId(chargeRecordId); + chargeRecord.setChargeProdId(chargeProdId); + chargeRecord.setUid(uid); + //chargeRecord.setRegion(region.byteValue()); + chargeRecord.setChannel(payChannel); + chargeRecord.setChargeStatus(Constant.ChargeRecordStatus.create); + // product中money单位为分 + chargeRecord.setAmount(chargeProd.getMoney()); + chargeRecord.setSubject(chargeProd.getProdName()); + chargeRecord.setBody(chargeProd.getProdName()); + chargeRecord.setClientIp(clientIp); + + //写入数据库 + chargeRecordService.insertChargeRecord(chargeRecord); + log.info("用户 {} 内购充值,本地订单号: {}", uid, chargeRecordId); + + //订单创建成功返回订单号 + AppInnerPayRecordDTO recordIdVo = new AppInnerPayRecordDTO(); + recordIdVo.setRecordId(chargeRecordId); + return recordIdVo; + + } catch (InterruptedException e) { + throw new ServiceException(BusiStatus.SERVERBUSY); + } finally { + if (lock.isLocked()){ + lock.unlock(); + } + } + + } + + private void validMoneyLimit(Long uid, String chargeProdId) { + GooglePayLimitConfigDTO config = getLimitConfig(); + Date now = new Date(); + Date beginTimeOfDay = DateTimeUtil.getBeginTimeOfDay(now); + Date endTimeOfDay = DateTimeUtil.getEndTimeOfDay(now); + + ChargeProd chargeProd = chargeProdService.getChargeProdById(chargeProdId); + if (chargeProd == null) { + log.error("充值产品不存在,prodId: {} ", chargeProdId); + throw new ServiceException(BusiStatus.CHARGE_PROD_NOT_EXIST); + } + + Long userChargeAmmount = chargeRecordService.getChargeUserAmountWithProdIds(Collections.singletonList(uid), config.getGoogleChargeProdIds(), beginTimeOfDay, endTimeOfDay); + log.info("{}在{}-{}时间段内使用google内购充值了{}", uid, DateTimeUtil.convertDate(beginTimeOfDay), DateTimeUtil.convertDate(endTimeOfDay), userChargeAmmount); + // 配置的单位是元,充值记录和产品的数据单位为分 + if (config.getLimitEveryOneDaySumAmount() * 100 < (chargeProd.getMoney() + userChargeAmmount)) { + throw new ServiceException(config.getErrorTip()); + } + Long allUserChargeAmount = chargeRecordService.getChargeUserAmountWithProdIds(null, config.getGoogleChargeProdIds(), beginTimeOfDay, endTimeOfDay); + log.info("全部用户{}-{}内使用google内购充值了{}", DateTimeUtil.convertDate(beginTimeOfDay), DateTimeUtil.convertDate(endTimeOfDay), allUserChargeAmount); + if (config.getLimitEveryDayAmount() * 100 < (chargeProd.getMoney() + allUserChargeAmount)) { + throw new ServiceException(config.getErrorTip()); + } + } + + private GooglePayLimitConfigDTO getLimitConfig() { + GooglePayLimitConfigDTO limitConfig = JSONObject.parseObject(sysConfService.getDefaultSysConfValueById(Constant.SysConfId.GOOGLE_PAY_LIMIT_CONFIG, "{}"), GooglePayLimitConfigDTO.class); + if (limitConfig.getLimitEveryDayAmount() == null) { + limitConfig.setLimitEveryDayAmount(9999999L); + } + if (limitConfig.getLimitEveryOneDaySumAmount() == null) { + limitConfig.setLimitEveryOneDaySumAmount(9999999L); + } + if (limitConfig.getGoogleChargeProdIds() == null) { + limitConfig.setGoogleChargeProdIds(Collections.emptyList()); + } + if (StringUtils.isEmpty(limitConfig.getErrorTip())) { + limitConfig.setErrorTip("充值失败,请联系客服处理"); + } + return limitConfig; + } + + public ChargeRecord verifyOrder(String chargeRecordId, String packageName, String googlePlayProdId, String purchaseToken) { + String lockKey = RedisKey.lock_pay_callback_notify.getKey(chargeRecordId); + RLock lock = redissonClient.getLock(lockKey); + try { + lock.tryLock(5L, TimeUnit.SECONDS); + + ChargeRecord chargeRecord = chargeRecordService.getChargeRecordById(chargeRecordId); + if (chargeRecord == null) { + log.error("[google play billing]充值记录不存在。chargeRecordId: {}", chargeRecordId); + throw new ServiceException(BusiStatus.RECORD_NOT_EXIST); + } + if (!Constant.ChargeRecordStatus.create.equals(chargeRecord.getChargeStatus()) && !Constant.ChargeRecordStatus.error.equals(chargeRecord.getChargeStatus())) { + log.info("[google play billing]订单状态不是创建或错误,不进行处理。chargeRecordId: {}", chargeRecordId); + throw new ServiceException(BusiStatus.RECORD_ALREADY_EXIST); + } + + if (!chargeRecord.getChargeProdId().equalsIgnoreCase(googlePlayProdId)) { + log.error("[google play billing]记录中的产品id和待查询内购产品id不一致。ChargeProdId: {}, googlePlayProdId: {}", chargeRecord.getChargeProdId(), googlePlayProdId); + throw new ServiceException(BusiStatus.RECORD_NOT_EXIST); + } + + ProductPurchase purchase = AndroidPublisherHelper.getPublisher().purchases().products().get(packageName, googlePlayProdId, purchaseToken).execute(); + log.info("purchase: {}", JSONObject.toJSONString(purchase)); + if (purchase == null) { + log.error("查询google购买记录返回为空。packageName: {}, prodId: {}, purchaseToken: {}", packageName, googlePlayProdId, purchaseToken); + throw new ServiceException(BusiStatus.RECORD_NOT_EXIST); + } + + if (!Constant.GooglePurchaseState.PURCHASED.equals(purchase.getPurchaseState())) { + log.error("[google play billing]订单未完成支付。当前状态: {}", purchase.getPurchaseState()); + throw new ServiceException(BusiStatus.RECORD_NOT_EXIST); + } + + chargeRecord.setPingxxChargeId(purchase.getOrderId()); + + return chargeRecord; + + } catch (Exception e) { + log.error("[google play billing]校验google内购失败", e); + if (e instanceof ServiceException) { + throw (ServiceException)e; + } else { + throw new ServiceException(BusiStatus.BUSIERROR); + } + } finally { + if (lock.isLocked()){ + lock.unlock(); + } + } + } + +} diff --git a/accompany-base/accompany-payment/src/main/java/com/accompany/payment/mapper/ChargeRecordMapperMgr.java b/accompany-base/accompany-payment/src/main/java/com/accompany/payment/mapper/ChargeRecordMapperMgr.java index 821d9ab27..245d2fe3b 100644 --- a/accompany-base/accompany-payment/src/main/java/com/accompany/payment/mapper/ChargeRecordMapperMgr.java +++ b/accompany-base/accompany-payment/src/main/java/com/accompany/payment/mapper/ChargeRecordMapperMgr.java @@ -66,4 +66,8 @@ public interface ChargeRecordMapperMgr { Long getHistoryRechargeAmountByChannel(@Param("userId") long userId, @Param("channel") String channel); + Long getChargeUserAmountWithProdIds(@Param("list") List uids, @Param("prodIds") List prodIds, + @Param("startTime") Date startTime, @Param("endTime") Date endTime); + + } diff --git a/accompany-base/accompany-payment/src/main/java/com/accompany/payment/service/ChargeRecordService.java b/accompany-base/accompany-payment/src/main/java/com/accompany/payment/service/ChargeRecordService.java index fc8073c18..7c3991d8e 100644 --- a/accompany-base/accompany-payment/src/main/java/com/accompany/payment/service/ChargeRecordService.java +++ b/accompany-base/accompany-payment/src/main/java/com/accompany/payment/service/ChargeRecordService.java @@ -173,4 +173,15 @@ public class ChargeRecordService extends BaseService { jedisLockService.unlock(RedisKey.ios__pay_user_toatl_amount_lock.getKey(userIdStr), lockVal); } } + + /** + * 获取当前时间段内用户充值金额 + **/ + public Long getChargeUserAmountWithProdIds(List uid, List prods, Date startDate, Date endDate) { + Long chargeUserAmountWithProdIds = chargeRecordMapperMgr.getChargeUserAmountWithProdIds(uid, prods, startDate, endDate); + if (chargeUserAmountWithProdIds == null) { + return 0L; + } + return chargeUserAmountWithProdIds; + } } diff --git a/accompany-base/accompany-payment/src/main/resources/mapper/ChargeRecordMapper.xml b/accompany-base/accompany-payment/src/main/resources/mapper/ChargeRecordMapper.xml index c336597fb..e3bb202e0 100644 --- a/accompany-base/accompany-payment/src/main/resources/mapper/ChargeRecordMapper.xml +++ b/accompany-base/accompany-payment/src/main/resources/mapper/ChargeRecordMapper.xml @@ -145,7 +145,8 @@ #{wxPubOpenid,jdbcType=VARCHAR}, #{subject,jdbcType=VARCHAR}, #{body,jdbcType=VARCHAR}, #{extra,jdbcType=VARCHAR}, #{metadata,jdbcType=VARCHAR}, #{chargeDesc,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP}) - + + insert into charge_record diff --git a/accompany-base/accompany-payment/src/main/resources/mapper/ChargeRecordMapperMgr.xml b/accompany-base/accompany-payment/src/main/resources/mapper/ChargeRecordMapperMgr.xml index 8cf50b7eb..bec76ba45 100644 --- a/accompany-base/accompany-payment/src/main/resources/mapper/ChargeRecordMapperMgr.xml +++ b/accompany-base/accompany-payment/src/main/resources/mapper/ChargeRecordMapperMgr.xml @@ -283,4 +283,19 @@ + + \ No newline at end of file diff --git a/accompany-business/accompany-business-web/src/main/java/com/accompany/business/controller/apppay/GooglePlayBillingChargeController.java b/accompany-business/accompany-business-web/src/main/java/com/accompany/business/controller/apppay/GooglePlayBillingChargeController.java new file mode 100644 index 000000000..53eb3190f --- /dev/null +++ b/accompany-business/accompany-business-web/src/main/java/com/accompany/business/controller/apppay/GooglePlayBillingChargeController.java @@ -0,0 +1,56 @@ +package com.accompany.business.controller.apppay; + +import com.accompany.business.service.ChargeService; +import com.accompany.common.annotation.Authorization; +import com.accompany.common.utils.IPUitls; +import com.accompany.core.enumeration.BusinessStatusCodeEnum; +import com.accompany.core.vo.BaseRequestVO; +import com.accompany.core.vo.BaseResponseVO; +import com.accompany.payment.dto.AppInnerPayRecordDTO; +import com.accompany.payment.google.GooglePlayBillingService; +import com.accompany.payment.model.ChargeProd; +import com.accompany.payment.model.ChargeRecord; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +@Api(tags = {"google内购"}, value = "google内购") +@RestController +@RequestMapping("/googlePlayBilling") +@Slf4j +public class GooglePlayBillingChargeController { + + @Autowired + private ChargeService chargeService; + @Autowired + private GooglePlayBillingService googlePlayBillingService; + + @ApiOperation("google内购预下单") + @PostMapping("/placeOrder") + @Authorization + public BaseResponseVO placeOrder(String chargeProdId, HttpServletRequest request) { + BaseRequestVO baseRequestVO = new BaseRequestVO(); + Long uid = baseRequestVO.getMyUserId(); + String clientIp = IPUitls.getRealIpAddress(request); + String deviceId = baseRequestVO.getDeviceId(); + + AppInnerPayRecordDTO appInnerPayRecordDTO = googlePlayBillingService.placeOrder(uid, chargeProdId, clientIp, deviceId); + + return new BaseResponseVO<>(BusinessStatusCodeEnum.SUCCESS, appInnerPayRecordDTO); + } + + @ApiOperation("google内购订单校验") + @PostMapping("/verifyOrder") + @Authorization + public BaseResponseVO verifyOrder(String chargeRecordId, String packageName, String googlePlayProdId, String purchaseToken) { + ChargeRecord chargeRecord = googlePlayBillingService.verifyOrder(chargeRecordId, packageName, googlePlayProdId, purchaseToken); + chargeService.updateAppPayData(chargeRecord); + return new BaseResponseVO<>(BusinessStatusCodeEnum.SUCCESS, BusinessStatusCodeEnum.SUCCESS.getReasonPhrase(), purchaseToken); + } +} diff --git a/accompany-business/accompany-business-web/src/test/java/servicetest/GooglePlayBillingServiceTest.java b/accompany-business/accompany-business-web/src/test/java/servicetest/GooglePlayBillingServiceTest.java new file mode 100644 index 000000000..ec6df0237 --- /dev/null +++ b/accompany-business/accompany-business-web/src/test/java/servicetest/GooglePlayBillingServiceTest.java @@ -0,0 +1,35 @@ +package servicetest; + +import com.accompany.payment.google.AndroidPublisherHelper; +import com.accompany.payment.google.GooglePlayBillingService; +import com.alibaba.fastjson.JSONObject; +import com.google.api.services.androidpublisher.model.ProductPurchase; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.IOException; + +public class GooglePlayBillingServiceTest extends CommonTest { + + @Autowired + private GooglePlayBillingService googlePlayBillingVerifyService; + + @Test + public void verifyOrderTest() throws Exception { + String chargeRecordId = "d9e457c4e7bc4d918d1a869bc49003fd "; + String packageName = "com.vele.ananplay"; + String prodId = "goods_cent_1499"; + String token ="lefpcedapihlpjcmjgnombhd.AO-J1OyZHdP6rv5D1sqFUyWsk1QbBuMS1oV4aTuet7CtgeKLZe_S6yjpOdkLpSTRT123gIQ8LSR7mWcayJtXCizwmMis7tqnTg"; + googlePlayBillingVerifyService.verifyOrder(chargeRecordId, packageName, prodId, token); + } + + @Test + public void getGooglePurchaseTest() throws IOException { + String packageName = "com.vele.ananplay"; + String prodId = "goods_cent_1499"; + String token ="lefpcedapihlpjcmjgnombhd.AO-J1OyZHdP6rv5D1sqFUyWsk1QbBuMS1oV4aTuet7CtgeKLZe_S6yjpOdkLpSTRT123gIQ8LSR7mWcayJtXCizwmMis7tqnTg"; + ProductPurchase purchase = AndroidPublisherHelper.getPublisher().purchases().products().get(packageName, prodId, token).execute(); + System.out.println(JSONObject.toJSONString(purchase)); + //System.out.println(JSONObject.parse(purchase.getObfuscatedExternalProfileId()).toString()); + } +}