谷歌支付服务端服务账号订单校验

茅星雨
2023-12-01

整个开发背景是前端在调用完google play v3 支付流程后,需要后台验证支付结果以及在自己的服务生成订单相关信息。对于服务账号的文档搜了度娘对于这块的资料少,无从下手,踩了不少坑。网上的搜出来的大多数是针对OAuth 2.0 客户端 ID的验证,需要使用到refreshAccess,但是服务账号不一样,那不需要验签通过后直接能请求查询订单。

这边服务端用的是java ,在接入googgle渠道中与安卓端对接的过程就三接口,1、登录校验 2、创建订单号及校验产品id后返回订单号给安卓端 3、支付完成订单校验,google支付那没有想微信那边支付成功后回调到我们服务端,它那边是通过安卓端的请求来通知我们自己的服务端的支付成功通知,掉单的情况也是客户端来通知。。

因这边用到的是消耗型的产品,后台就省了一步acknowledge的确认,只需要校验订单支付状态。

登入前期准备以下只是格式,具体的参数从谷歌后台下载:
1、客户端秘钥,其中google_client_secret_data是我这边自己加的

{"google_client_secret_data": {
		"web": {
			"client_id": "123456789-1li1d7k.apps.googleusercontent.com",
			"project_id": "project_id-0310",
			"auth_uri": "https://accounts.google.com/o/oauth2/auth",
			"token_uri": "https://oauth2.googleapis.com/token",
			"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs"
		}
	}
}

2、服务端秘钥其中google_service_account_data是我这边自己加的

{
	"google_service_account_data": {
		"type": "service_account",
		"project_id": "123456789526755524298661-783",
		"private_key_id": "1234567890ac89f9c70f245f1798587ae9b7",
		"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQASCBKcwggSjAgEAAoIBAQCX1kMC4Au/\n-----END PRIVATE KEY-----\n",
		"client_email": "test02661-783.iam.gserviceaccount.com",
		"client_id": "123456789",
		"auth_uri": "https://accounts.google.com/o/oauth2/auth",
		"token_uri": "https://oauth2.googleapis.com/token",
		"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
		"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/83.iam.gserviceaccount.com"
	}
}

用到的 maven 依赖jar包

 <dependency>
        <groupId>com.google.api-client</groupId>
         <artifactId>google-api-client</artifactId>
         <version>1.32.1</version>
</dependency>
 <dependency>
        <groupId>com.google.auth</groupId>
        <artifactId>google-auth-library-oauth2-http</artifactId>
        <version>1.2.2</version>
</dependency>
<dependency>
        <groupId>com.google.apis</groupId>
        <artifactId>google-api-services-androidpublisher</artifactId>
        <version>v3-rev20211021-1.32.1</version>
 </dependency>

登录校验

private ResultData<Object> googleIdTokenVerifier(JSONObject google_client_secret_dataJson,String idTokenString) throws IOException,java.security.GeneralSecurityException{
        
        
        GoogleClientSecrets clientSecrets = GsonFactory.getDefaultInstance().fromString(
                google_client_secret_dataJson.get("google_client_secret_data").toString(),
                GoogleClientSecrets.class);
       String CLIENT_ID=clientSecrets.getDetails().getClientId();
       logger.info("CLIENT_ID={}",CLIENT_ID);
        GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance())
                // Specify the CLIENT_ID of the app that accesses the backend:
                .setAudience(Collections.singletonList(CLIENT_ID))
                // Or, if multiple clients access the backend:
                //.setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3))
                .build();

        GoogleIdToken idToken = verifier.verify(idTokenString);
        logger.info("验证结果idToken={}",idToken);
        if (idToken == null) {
            logger.error("IDtoken验证失败!,Invalid ID token.");
            return ResultData.fail("idToken验证失败");
        }
            Payload payload = idToken.getPayload();
            logger.info("验证结果payload={}",payload);
            // Print user identifier
            String googleUserId = payload.getSubject();
            logger.info("googleUserId={}",googleUserId);
           
           
            return ResultData.success(googleUserId);
    }

订单创建

略。。

订单成功校验

 private ResultData googlePayNotify(JSONObject google_service_account_dataJson,HttpServletRequest request) {

        Map<String, String[]> parameterMap = request.getParameterMap();
       
        logger.info("支付回调返回参数:{}", JSONObject.toJSONString(parameterMap));
        

        JSONObject google_service_account_data  = google_service_account_dataJson.getJSONObject("google_service_account_data");
        logger.info("google_service_account_data={}",google_service_account_data);
        String myOrderId = parameterMap.get("myOrderId")[0] ;  //自己的订单号
        String purchaseToken = parameterMap.get("purchaseToken")[0];  //购买应用内产品时提供给用户设备的令牌。
        String productId = parameterMap.get("productId")[0];  //产品id
        String orderId = parameterMap.get("orderId")[0]; //谷歌订单号
        String channelType = parameterMap.get("channelType")[0]; //支付渠道
        String amount = parameterMap.get("amount")[0]; //金额
        String packageName = parameterMap.get("packageName")[0]; //包名 安卓端传

        //获取渠道商品ID 校验
        
		
        
        PayOrder payOrder =  payOrderDao.getPayOrder(myOrderId);


        /** 响应结果
         {
         "resource": {
         object (ProductPurchase)
         }
         }
         ProductPurchase:
         {
         "kind": string, //这种表示 androidpublisher 服务中的一个 inappPurchase 对象
         "purchaseTimeMillis": string,//购买产品的时间,自纪元(1970 年 1 月 1 日)以来的毫秒数。
         "purchaseState": integer,//订单的购买状态。可能的值为:0. 已购买 1. 已取消 2. 待定
         "consumptionState": integer,//inapp产品的消费状态。可能的值为: 0. 尚未消耗 1. 已消耗
         "developerPayload": string,//包含有关订单的补充信息的开发人员指定的字符串。
         "orderId": string,//与购买 inapp 产品关联的订单 ID
         "purchaseType": integer,//inapp 产品的购买类型。仅当此购买不是使用标准应用内结算流程进行时才设置此字段。可能的值为: 0. 测试(即从许可测试帐户购买) 1. 促销(即使用促销代码购买) 2. 奖励(即通过观看视频广告而不是付费)
         "acknowledgementState": integer,//inapp 产品的确认状态。可能的值为: 0. 尚未确认 1. 确认
         "purchaseToken": string,//为识别此次购买而生成的购买令牌。
         "productId": string,//inapp 产品 SKU。
         "quantity": integer,//与购买 inapp 产品相关的数量。
         "obfuscatedExternalAccountId": string,//与您应用中的用户帐户唯一关联的 id 的混淆版本。仅在购买时使用https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder#setobfuscatedaccountid指定时出现。
         "obfuscatedExternalProfileId": string,//与您应用中的用户个人资料唯一关联的 id 的混淆版本。仅在购买时使用https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder#setobfuscatedprofileid指定时出现。
         "regionCode": string //授予产品时用户的 ISO 3166-1 alpha-2 计费区域代码。
         }
         */
        /**
         * google 验征订单
         */
          /*** 注意
         * 		秘钥格式"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoI\n-----END PRIVATE KEY-----\n",
         * 添加秘钥空格,存数据库后转json秘钥空格被去掉,没有空格时将导致校验错误
         */
        String credentialsJson = google_service_account_data.toJSONString();
        credentialsJson = credentialsJson.replace("BEGINPRIVATEKEY","BEGIN PRIVATE KEY");
        credentialsJson = credentialsJson.replace("ENDPRIVATEKEY","END PRIVATE KEY");
        logger.info("添加秘钥空格,存数据库后转json秘钥空格被去掉,没有空格时将导致校验错误!!!,credentialsJson=",credentialsJson);
     
        InputStream stream = new ByteArrayInputStream(credentialsJson.getBytes());
        ProductPurchase purchase = null;
        try{
            GoogleCredentials credentials = GoogleCredentials.fromStream(stream);
            credentials = credentials.createScoped(Arrays.asList("https://www.googleapis.com/auth/androidpublisher")); //订单查询域
            logger.info("credentials ={}",credentials);

            credentials.refreshIfExpired();
            logger.info("credentials.refreshIfExpired={}",credentials);
            // AccessToken token = credentials.getAccessToken();
            // OR
            //  AccessToken refreshAccess = credentials.refreshAccessToken();
           //该方法已弃用 GoogleCredential googleCredential;
		   
            HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);
            AndroidPublisher publisher = new AndroidPublisher.Builder(new NetHttpTransport(),
                    GsonFactory.getDefaultInstance(), requestInitializer).build();
            AndroidPublisher.Purchases.Products products = publisher.purchases()
                    .products();
			//订单查询
            AndroidPublisher.Purchases.Products.Get product = products.get(packageName,productId,purchaseToken);

             purchase = product.execute();
            logger.info("查询订单结果purchase={}",purchase);

        }catch (Exception e){
            logger.error("查询订单失败!",e);
            return  ResultData.fail("查询订单失败!");
        }

        logger.info("订单状态为(0.已购买1.已取消2.待定)purchaseState={},消费状态(0.尚未消耗1.已消耗)consumptionState={}," +
                "确认状态(0.尚未确认1.确认)acknowledgementState={}",purchase.getPurchaseState(),
                purchase.getConsumptionState(),
                purchase.getAcknowledgementState());

        logger.info("订单发货状态ayOrder.getDeliveryStatus={}",payOrder.getDeliveryStatus());
        // 订单的购买状态:(0.已购买1.已取消2.待定)已购买   非消耗产品不用校验acknowledgementState状态
        if (purchase.getPurchaseState() != 0) {
            logger.error("订单未支付!");
            return  ResultData.fail("订单未支付");
        }

		//保存purchaseToken字段
        
        //ConsumptionState=1已消耗  发货状态为已发货
        if(1 == purchase.getConsumptionState() && DELIVERY_STATUS_SUCCESS == payOrder.getDeliveryStatus()){
            return  ResultData.fail("重复回调!,已发货!");
        }

      

        String issueOrderNo = gameOrderId;
        String snOrderNo = purchase.getOrderId();

        //.通知发货...
       
        return ResultData.success();    

    }
 类似资料: