Java服务端实现Google Pay支付功能

2023-11-28 01:10

本文主要是介绍Java服务端实现Google Pay支付功能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Hello大家好,鉴于之前第一篇的博客关于Google pay支付,可能很多读者对整体功能不太清晰,然后最近对代码进行了重构,希望接下来这篇能够帮助读者们更快捷、更方便的集成Google Pay功能,好了话不多说,直接进入正题:

Google支付所需配置

可参考官网,创建应用、添加权限、生成json文件,这里我就不详细多说了,这种配置获取可根据官网、或者网上找的方法一大堆。像有些读者出现调用API时出现400的错误,基本都是配置错误导致的,主要是权限没配置好。

谷歌支付回调

谷歌开发这通知功能(Pub/Sub),这个我也不详细多说了,可参考官网。(如果不懂可以看之前的博客有参考地址)

好的接下来咱们直接进入代码层面,先说代码层面之前,我先给大家梳理下流程:
支付流程图
下单逻辑读者们应该没啥问题吧,注意看里面的订单处理(1)和订单处理(2),分为客户端主动回调请求订单处理和Google回调服务端通知订单处理,订单处理(1)相信流程大家都没问题,但是订单处理(2),大家想想,如果Google回调通知订单,服务端怎么获取该用户发起的系统订单号呢?注意:Google下单时时支持字段透传的,也就是说,在步骤③的时候吧步骤②返回的系统订单号放入,那么这个Google支付完成后,回调Google将会透传这个字段给服务端,这个下面代码会讲(各位读者不要急)~,那这样这个回调通知是不是就产生了Google订单和系统订单关联关系了!

接下来看代码实现,具体下单逻辑我就不讲述了,这是读者们实现的,以下会讲解怎么集成Google Pay API。

代码集成实现

1、所需依赖
Google pay SDK依赖和认证依赖:

<!-- google pay依赖(使用最新版本)可取maven仓库查看,这边是GitHub发布的新版本 --><dependency><groupId>com.google.apis</groupId><artifactId>google-api-services-androidpublisher</artifactId><version>v3-rev20230815-2.0.0</version></dependency>
<!-- google client依赖 https://mvnrepository.com/artifact/com.google.auth/google-auth-library-oauth2-http --><dependency><groupId>com.google.auth</groupId><artifactId>google-auth-library-oauth2-http</artifactId><version>1.19.0</version></dependency>

2、Client对象创建

	    //存放多个实例的 AndroidPublisher Mappublic static Map<String, AndroidPublisher> ANDROID_PUBLISHER_MAP = new ConcurrentHashMap<>();/*** 必须实现对象创建逻辑,可能会存在空** @param appName 应用唯一标识(单例模式创建)* @return*/@Overridepublic synchronized AndroidPublisher getAuthInstance(String appName) throws PaymentCoreException {AndroidPublisher androidPublisher = ANDROID_PUBLISHER_MAP.get(appName);if (androidPublisher != null) {return androidPublisher;}//工厂策略模式读取,这个是我存放的Google配置信息,大家可忽略,可自己实现GooglePayConfigEntity googlePayConfig = paymentConfigFactory.getPaymentConfig(ConstantUtil.GOOGLE_PAYMENT_CONFIG_IMPL, appName);InputStream input = null;try {//获取用户凭证 json文件流input = FileUtil.getImageStreamByUrl(googlePayConfig.getJsonFileUrl());//生成Credentials对象GoogleCredentials credentials = GoogleCredentials.fromStream(input);HttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();HttpCredentialsAdapter httpCredentialsAdapter = new HttpCredentialsAdapter(credentials);//创建 AndroidPublisher对象androidPublisher = new AndroidPublisher.Builder(transport,GsonFactory.getDefaultInstance(),httpCredentialsAdapter).build();} catch (Exception e) {throw new PaymentCoreException("[Google] client instance create exception: " + e.getMessage());} finally {FileUtil.closeStream(input);}if (androidPublisher != null) {//放入对象容器ANDROID_PUBLISHER_MAP.put(appName, androidPublisher);}return androidPublisher;}

3、以下是各种API处理方法:

    /*** 一次性(单次)购买订单对象获取** @param appName       应用唯一标识* @param packageName   包名称* @param productId     商品Id* @param purchaseToken 谷歌购买凭据* @return*/public ProductPurchase getOneTimeGoogleOrder(String appName, String packageName, String productId, String purchaseToken) throws PaymentCoreException {AndroidPublisher authInstance = this.getAuthInstance(appName);if (authInstance == null) {throw new PaymentCoreException("[Google] AndroidPublisher instance is empty");}ProductPurchase products = null;try {products = authInstance.purchases().products().get(packageName, productId, purchaseToken).execute();log.info("Products order info: {}", JSONUtil.toJsonStr(products));} catch (IOException e) {throw new PaymentCoreException("[Google] Get google products order info exception: " + e.getMessage());}return products;}/*** 订阅购买订单获取(V2版本),最新订阅请使用V2,官方对V1已经不维护了,但是可以用哈!** @param appName       应用唯一标识* @param packageName   包名称* @param purchaseToken 购买凭据* @return*/public SubscriptionPurchaseV2 getSubscriptionGoogleOrderV2(String appName, String packageName, String purchaseToken) throws PaymentCoreException {AndroidPublisher authInstance = this.getAuthInstance(appName);if (authInstance == null) {throw new PaymentCoreException("[Google] AndroidPublisher instance is empty");}SubscriptionPurchaseV2 subscriptionsV2 = null;try {subscriptionsV2 = authInstance.purchases().subscriptionsv2().get(packageName, purchaseToken).execute();log.info("SubscriptionsV2 order info: {}", JSONUtil.toJsonStr(subscriptionsV2));} catch (IOException e) {throw new PaymentCoreException("[Google] Get google subscriptions v2 order info exception: " + e.getMessage());}return subscriptionsV2;}/*** 取消订阅方法** @param appName        应用唯一标识* @param packageName    包名称* @param subscriptionId 订阅Id,其实就是商品Id* @param purchaseToken  购买凭据* @return*/public void cancelSubscription(String appName, String packageName, String subscriptionId, String purchaseToken) throws PaymentCoreException {AndroidPublisher authInstance = this.getAuthInstance(appName);if (authInstance == null) {throw new PaymentCoreException("[Google] AndroidPublisher instance is empty");}try {authInstance.purchases().subscriptions().cancel(packageName, subscriptionId, purchaseToken).execute();log.info("Cancel subscription success");} catch (IOException e) {throw new PaymentCoreException("[Google] Cancel google subscriptions exception: " + e.getMessage());}}/*** 订阅退款方法** @param appName        应用唯一标识* @param packageName    包名称* @param subscriptionId 订阅Id,其实就是商品Id* @param purchaseToken  购买凭据* @return*/public void refundSubscription(String appName, String packageName, String subscriptionId, String purchaseToken) throws PaymentCoreException {AndroidPublisher authInstance = this.getAuthInstance(appName);if (authInstance == null) {throw new PaymentCoreException("[Google] AndroidPublisher instance is empty");}try {authInstance.purchases().subscriptions().refund(packageName, subscriptionId, purchaseToken).execute();log.info("Refund subscription success");} catch (IOException e) {throw new PaymentCoreException("[Google] Refund google subscriptions exception: " + e.getMessage());}}/*** 订阅撤销方法,但是不建议我们实现,让客户自己在Google play商店操作订阅的状态** @param appName        应用唯一标识* @param packageName    包名称* @param subscriptionId 订阅Id,其实就是商品Id* @param purchaseToken  购买凭据* @return*/public void revokeSubscription(String appName, String packageName, String subscriptionId, String purchaseToken) throws PaymentCoreException {AndroidPublisher authInstance = this.getAuthInstance(appName);if (authInstance == null) {throw new PaymentCoreException("[Google] AndroidPublisher instance is empty");}try {authInstance.purchases().subscriptions().revoke(packageName, subscriptionId, purchaseToken).execute();log.info("Revoke subscription success");} catch (IOException e) {throw new PaymentCoreException("[Google] Revoke google subscriptions exception: " + e.getMessage());}return;}/*** 订阅购买订单获取(旧版本),跟V2一样的逻辑, 但是google官网说明方法已经废弃,不维护** @param appName        应用唯一标识* @param packageName    包名称* @param subscriptionId 订阅Id,其实就是商品Id* @param purchaseToken  购买凭据* @return*/public SubscriptionPurchase getSubscriptionGoogleOrder(String appName, String packageName, String subscriptionId, String purchaseToken) throws PaymentCoreException {AndroidPublisher authInstance = this.getAuthInstance(appName);if (authInstance == null) {throw new PaymentCoreException("[Google] AndroidPublisher instance is empty");}SubscriptionPurchase subscriptions = null;try {subscriptions = authInstance.purchases().subscriptions().get(packageName, subscriptionId, purchaseToken).execute();log.info("Subscriptions order info: {}", JSONUtil.toJsonStr(subscriptions));} catch (IOException e) {throw new PaymentCoreException("[Google] Get google subscriptions order info exception: " + e.getMessage());}return subscriptions;}

4、介绍以下重点参数:
appName: 用于SaaS环境区分项目的,一个环境一个appName。
packageName: Google配置的包应用名称。客户端传入。
subscriptionId: Google配置的商品Id,唯一标识。客户端传入。
purchaseToken: 用户购买的凭证。客户端传入。

5、判断订单是否成功:

单次(消耗):

/*** @author xxx  应用内商品的消耗状态。可能的值包括:0。尚未消耗 1. 已使用 (Products对象中的值)* @version 1.0* @date 2023/8/23 20:13*/
public enum ConsumptionStateEnum {NOT_CONSUMED(0, "Yet to be consumed"),CONSUMED(1, "Consumed");public int code;public String msg;ConsumptionStateEnum(int code, String msg) {this.code = code;this.msg = msg;}public int getCode() {return code;}public String getMsg() {return msg;}
}/*** @author xxx  订单的购买状态 可能的值包括:0。购买 1. 已取消 2. 待处理 (Products对象中的值)* @version 1.0* @date 2023/8/23 19:59*/
public enum PurchaseStateEnum {PURCHASED(0, "Purchased"),CANCELED(1, "Canceled"),PENDING(2, "Pending");public int code;public String msg;PurchaseStateEnum(int code, String msg) {this.code = code;this.msg = msg;}public int getCode() {return code;}public String getMsg() {return msg;}
}/*** 校验单次购买是否成功** @param productPurchase* @return*/public boolean checkOneTimeGoogleOrderSuccess(ProductPurchase productPurchase) {if (ConsumptionStateEnum.CONSUMED.getCode() == productPurchase.getConsumptionState()&& PurchaseStateEnum.PURCHASED.getCode() == productPurchase.getPurchaseState()) {return true;}return false;}/*** 校验单次购买是否失败** @param productPurchase* @return*/public boolean checkOneTimeGoogleOrderFail(ProductPurchase productPurchase) {if (PurchaseStateEnum.CANCELED.getCode() == productPurchase.getPurchaseState()) {return true;}return false;}

订阅

/*** @author xxx订阅的确认状态。 (subscriptionsv2)对象中的 acknowledgementState* ACKNOWLEDGEMENT_STATE_UNSPECIFIED	未指定的确认状态。* ACKNOWLEDGEMENT_STATE_PENDING	订阅尚未确认。* ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED	已确认订阅。* @version 1.0* @date 2023/8/23 20:55*/
public enum SubscriptionAckStateEnum {ACKNOWLEDGEMENT_STATE_UNSPECIFIED("ACKNOWLEDGEMENT_STATE_UNSPECIFIED", "Unspecified acknowledgement state"),ACKNOWLEDGEMENT_STATE_PENDING("ACKNOWLEDGEMENT_STATE_PENDING", "The subscription is not acknowledged yet"),ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED("ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED", "The subscription is acknowledged");public String code;public String msg;SubscriptionAckStateEnum(String code, String msg) {this.code = code;this.msg = msg;}public String getCode() {return code;}public String getMsg() {return msg;}
}/*** @author xxx订阅的当前状态。 (subscriptionsv2)对象中的 subscriptionState* SUBSCRIPTION_STATE_UNSPECIFIED	未指定订阅状态。* SUBSCRIPTION_STATE_PENDING	已创建订阅,但在注册期间正在等待付款。在此状态下,所有商品均正在等待付款。* SUBSCRIPTION_STATE_ACTIVE	订阅处于有效状态。- (1) 如果订阅是自动续订型方案,则至少有一项是自动续订状态并且未过期。- (2) 如果订阅是预付费方案,至少有一项不会过期。* SUBSCRIPTION_STATE_PAUSED	订阅已暂停。只有在订阅是自动续订型方案时,才会显示该状态。在此状态下,所有商品均处于暂停状态。* SUBSCRIPTION_STATE_IN_GRACE_PERIOD	订阅处于宽限期。只有在订阅是自动续订型方案时,才会显示该状态。在此状态下,所有商品均处于宽限期。* SUBSCRIPTION_STATE_ON_HOLD	订阅已暂停(已暂停)。只有在订阅是自动续订型方案时,才会显示该状态。在此状态下,所有商品均处于保全状态。* SUBSCRIPTION_STATE_CANCELED	订阅已取消,但尚未过期。只有在订阅是自动续订型方案时,才会显示该状态。所有商品的 autoRenewEnabled 都设为 false。* SUBSCRIPTION_STATE_EXPIRED	订阅已过期。所有内容的过期时间都是过去的时间。* @version 1.0* @date 2023/8/23 20:37*/
public enum SubscriptionStateEnum {SUBSCRIPTION_STATE_UNSPECIFIED("SUBSCRIPTION_STATE_UNSPECIFIED", "Unspecified subscription state"),SUBSCRIPTION_STATE_PENDING("SUBSCRIPTION_STATE_PENDING", "Subscription was created"),SUBSCRIPTION_STATE_ACTIVE("SUBSCRIPTION_STATE_ACTIVE", "Subscription is active"),SUBSCRIPTION_STATE_PAUSED("SUBSCRIPTION_STATE_PAUSED", "Subscription is paused"),SUBSCRIPTION_STATE_IN_GRACE_PERIOD("SUBSCRIPTION_STATE_IN_GRACE_PERIOD", "Subscription is in grace period"),SUBSCRIPTION_STATE_ON_HOLD("SUBSCRIPTION_STATE_ON_HOLD", "Subscription is on hold (suspended)"),SUBSCRIPTION_STATE_EXPIRED("SUBSCRIPTION_STATE_EXPIRED", "Subscription is expired"),SUBSCRIPTION_STATE_CANCELED("SUBSCRIPTION_STATE_CANCELED", "Subscription is canceled but not expired yet");public String code;public String msg;SubscriptionStateEnum(String code, String msg) {this.code = code;this.msg = msg;}public String getCode() {return code;}public String getMsg() {return msg;}}/*** 校验订阅订单是否支付成功** @param subscriptionsV2* @return*/public boolean checkSubscriptionSuccess(SubscriptionPurchaseV2 subscriptionsV2) {String acknowledgementState = subscriptionsV2.getAcknowledgementState();if (SubscriptionAckStateEnum.ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED.getCode().equals(acknowledgementState)) {return true;}return false;}

以上就是集成的代码实现。

回调处理

可看官网文档,但是这边我都给你弄成JavaBean了,也没必要去看(以下忽略getter/setter):

回调实体:

/*** @author xxx 谷歌开发者通知对象* @version 1.0* @date 2023/10/8 16:12*/
public class DeveloperNotification implements Serializable {/*** 此通知的版本。最初,此值为“1.0”。此版本与其他版本字段不同。*/private String version;/*** 与此通知相关的应用的软件包名称(例如“com.some.thing”)。*/private String packageName;/*** 事件发生的时间戳,以从公元纪年开始计算的毫秒数表示。*/private long eventTimeMillis;/*** 如果此字段存在,则此通知与订阅相关,并且此字段包含与订阅相关的其他信息。请注意,此字段与 testNotification 和 oneTimeProductNotification 互斥。*/private SubscriptionNotification subscriptionNotification;/*** 如果此字段存在,则此通知与一次性购买相关,并且此字段包含与购买交易相关的其他信息。请注意,此字段与 testNotification 和 subscriptionProductNotification 互斥。*/private OneTimeProductNotification oneTimeProductNotification;/*** 如果此字段存在,则此通知与测试发布相关。这些只通过 Google Play 管理中心发送。请注意,此字段与 subscriptionNotification 和 oneTimeProductNotification 互斥。*/private TestNotification testNotification;
}/*** @author xxx 谷歌开发者回调订阅通知信息* @version 1.0* @date 2023/10/8 15:55*/
public class SubscriptionNotification implements Serializable {/*** 通知的版本。最初,此值为“1.0”。此版本与其他版本字段不同。*/private String version;/*** 订阅的 notificationType 可以具有以下值:* (1) SUBSCRIPTION_RECOVERED - 从帐号保留状态恢复了订阅。* (2) SUBSCRIPTION_RENEWED - 续订了处于活动状态的订阅。* (3) SUBSCRIPTION_CANCELED - 自愿或非自愿地取消了订阅。如果是自愿取消,在用户取消时发送。* (4) SUBSCRIPTION_PURCHASED - 购买了新的订阅。* (5) SUBSCRIPTION_ON_HOLD - 订阅已进入帐号保留状态(如果已启用)。* (6) SUBSCRIPTION_IN_GRACE_PERIOD - 订阅已进入宽限期(如果已启用)。* (7) SUBSCRIPTION_RESTARTED - 用户已通过 Play > 帐号 > 订阅恢复了订阅。订阅已取消,但在用户恢复时尚未到期。如需了解详情,请参阅恢复。* (8) SUBSCRIPTION_PRICE_CHANGE_CONFIRMED - 用户已成功确认订阅价格变动。* (9) SUBSCRIPTION_DEFERRED - 订阅的续订时间点已延期。* (10) SUBSCRIPTION_PAUSED - 订阅已暂停。* (11) SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED - 订阅暂停计划已更改。* (12) SUBSCRIPTION_REVOKED - 用户在到期时间之前已撤消订阅。* (13) SUBSCRIPTION_EXPIRED - 订阅已到期。*/private int notificationType;/*** 购买订阅时向用户设备提供的令牌。*/private String purchaseToken;/*** 所购买订阅的商品 ID(例如“monthly001”)。*/private String subscriptionId; 
}    /*** @author xxx 谷歌开发者回调单次通知信息* @version 1.0* @date 2023/10/8 16:09*/
public class OneTimeProductNotification implements Serializable {/*** 通知的版本。最初,此值为“1.0”。此版本与其他版本字段不同。*/private String version;/*** 通知的类型。它可以具有以下值:* (1) ONE_TIME_PRODUCT_PURCHASED - 用户成功购买了一次性商品。* (2) ONE_TIME_PRODUCT_CANCELED - 用户已取消待处理的一次性商品购买交易。*/private int notificationType;/*** 购买订阅时向用户设备提供的令牌。*/private String purchaseToken;/*** 购买的一次性商品的商品 ID(例如“sword_001”)。*/private String sku;
}/*** @author xxx 订阅通知类型,具体描述可参考SubscriptionNotification实体* @version 1.0* @date 2023/10/8 19:15*/
public enum SubscriptionNotifyTypeEnum {SUBSCRIPTION_RECOVERED(1),SUBSCRIPTION_RENEWED(2),SUBSCRIPTION_CANCELED(3),SUBSCRIPTION_PURCHASED(4),SUBSCRIPTION_ON_HOLD(5),SUBSCRIPTION_IN_GRACE_PERIOD(6),SUBSCRIPTION_RESTARTED(7),SUBSCRIPTION_PRICE_CHANGE_CONFIRMED(8),SUBSCRIPTION_DEFERRED(9),SUBSCRIPTION_PAUSED(10),SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED(11),SUBSCRIPTION_REVOKED(12),SUBSCRIPTION_EXPIRED(13);private int type;SubscriptionNotifyTypeEnum(int type) {this.type = type;}public int getType() {return type;}
}    

Google回调接口实现:

/*** 谷歌回调通知,订单处理** @param body*/@RequestMapping(value = "/notify/payment/google", method = RequestMethod.POST)public void googleNotify(@RequestBody(required = false) byte[] body) {String bodyStr = new String(body, "utf-8");//转成JSON/**  参数信息*{*   "message": {*     "attributes": {*       "key": "value"*     },*     "data": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",*     "messageId": "136969346945"*   },*   "subscription": "projects/myproject/subscriptions/mysubscription"* }*/JSONObject bodyJson = JSONObject.parseObject(URLDecoder.decode(bodyStr, "utf-8"));//base64解码dataString data = EncryptionUtil.base64Decode(bodyJson.getJSONObject("message").getString("data"));DeveloperNotification developerNotification = JSONObject.parseObject(data, DeveloperNotification.class);//判断developerNotification对象中有哪个对象if (subscriptionNotification != null) {//为订阅通知,调用上述的方法获取订单校验即可}if (oneTimeProductNotification != null) {//为单次通知,调用上述的方法获取订单校验即可}}

订阅订单处理:

注意,里面包含了流程图中订单处理(2)获取系统订单的方式

/*** 订阅回调通知** @param subscriptionNotification* @param packageName* @param appName*/private void subscriptionNotify(SubscriptionNotification subscriptionNotification, String packageName, String appName) {int notificationType = subscriptionNotification.getNotificationType();String purchaseToken = subscriptionNotification.getPurchaseToken();//获取订阅订单信息try {SubscriptionPurchaseV2 subscriptionGoogleOrderV2 = getSubscriptionGoogleOrderV2(appName, packageName, purchaseToken);//判断订单状态if (!googlePayment.checkSubscriptionSuccess(subscriptionGoogleOrderV2)) {return;}//判断通知类型if (SubscriptionNotifyTypeEnum.SUBSCRIPTION_PURCHASED.getType() == notificationType) {//购买了新订阅,如果是上面流程图(2)处理,教你们怎么获取系统订单,让客户端设置相应的属性即可ExternalAccountIdentifiers externalAccountIdentifiers = subscriptionGoogleOrderV2.getExternalAccountIdentifiers();String obfuscatedExternalAccountId = externalAccountIdentifiers.getObfuscatedExternalAccountId();String obfuscatedExternalProfileId = externalAccountIdentifiers.getObfuscatedExternalProfileId();String externalAccountId = externalAccountIdentifiers.getExternalAccountId();responseVO = newSubscription(subscriptionGoogleOrderV2);}if (SubscriptionNotifyTypeEnum.SUBSCRIPTION_CANCELED.getType() == notificationType) {//取消了订阅responseVO = cancelSubscription(subscriptionGoogleOrderV2);}if (SubscriptionNotifyTypeEnum.SUBSCRIPTION_RENEWED.getType() == notificationType) {//续订了订阅//根据现在请求谷歌的googleOrderId获取续订包的订单,获取用户跟productId//续订的谷歌订单是有规则的,比如你第一次购买产生的订单是GPA.123-456-789,那第一次续订的产生的订单号是GPA.123-456-789..0,第二次GPA.123-456-789..1,依次类推。//续订这里还有一个坑,当订阅包降级购买的时候,谷歌并不是通知你购买了新订阅包而是续订,比如你购买的高级订阅包是GPA.123-456-789,当你切换低级的订阅包的时候订单号为GPA.111-222-333..0,你会发现订单号变了,这时候就难办了,拿不到之前用户的信息(谷歌返回的订单参数是没有项目里面用户信息的),经过联调发现,他们两个订单的linkedPurchaseToken是一样的,可以参考上图中参数的介绍//当降级和自动续订的时候都会走这个通知,所以里面有两个逻辑,需要自己去判断,这两者的不同上面都写的很清楚了。responseVO = renewSubscription(subscriptionGoogleOrderV2);}if (SubscriptionNotifyTypeEnum.SUBSCRIPTION_ON_HOLD.getType() == notificationType) {//订阅进入保留状态,用户拒绝付款responseVO = onHoldSubscription(subscriptionGoogleOrderV2);}if (SubscriptionNotifyTypeEnum.SUBSCRIPTION_RECOVERED.getType() == notificationType) {//从保留状态恢复了订阅responseVO = recoverSubscription(subscriptionGoogleOrderV2);}if (SubscriptionNotifyTypeEnum.SUBSCRIPTION_RESTARTED.getType() == notificationType) {//重新注册了订阅,用户已通过“Play”>“帐号”>“订阅”重新激活其订阅(需要选择使用订阅恢复功能)responseVO = restartSubscription(subscriptionGoogleOrderV2);}if (SubscriptionNotifyTypeEnum.SUBSCRIPTION_REVOKED.getType() == notificationType) {//用户撤销订阅responseVO = revokeSubscription(subscriptionGoogleOrderV2);}if (SubscriptionNotifyTypeEnum.SUBSCRIPTION_EXPIRED.getType() == notificationType) {//订阅过期responseVO = expiredSubscription(subscriptionGoogleOrderV2);}if (SubscriptionNotifyTypeEnum.SUBSCRIPTION_IN_GRACE_PERIOD.getType() == notificationType) {//订阅进入宽限期responseVO = inGracePeriodSubscription(subscriptionGoogleOrderV2);}if (SubscriptionNotifyTypeEnum.SUBSCRIPTION_PAUSED.getType() == notificationType) {//订阅暂停responseVO = pauseSubscription(subscriptionGoogleOrderV2);}} catch (PaymentCoreException e) {log.error("Google get subscription order v2 exception: {}", e.getMessage());e.printStackTrace();} finally {//记录处理结果}}

所有的代码均已讲解,各位读者对这个支付功能还有什么疑惑呢,在评论区探讨的,码字不易~讲解不易!!!点个赞再走。记住,我还是那个会撩头发的程序猿!!!

转载请标明出处谢谢大家~

这篇关于Java服务端实现Google Pay支付功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/428816

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�