google play支付接入(java版本)

3,832 阅读3分钟

前言

最近公司要做海外版,需要接入google play.踩了不少坑,特作此文

20180809更新,增加导包,参数示例数据

20190106更新, google jar包v2版本已废弃,api未变动,更新至v3即可

总体思路

  1. 验证方法1 rsa签名验证

请参考这篇

  1. 验证方法2 请求google api验证

需要在google developer console申请service account,具体参考这篇

我的做法 我比较diao,两种都上了(手动滑稽) 其实是因为之前我认为publicKey在客户端也存在,可能有风险,真正明白后才知道rsa的公匙本来就是可以可以公开的,私匙是有google有,肯定是安全的,但是代码已经上线了,也浪费不了多少资源,算是双重保险吧

放码过来

maven依赖

<dependency>
			<groupId>com.google.apis</groupId>
			<artifactId>google-api-services-androidpublisher</artifactId>
			<version>v2-rev44-1.22.0</version>
		</dependency>

充值接口,在这里进行充值操作

public Map<String, Object> vertifyOrder(
			@RequestParam("purchaseData") String purchaseData,
			@RequestParam("signature") String signature,
			@RequestParam("type") int type) {

拦截器,通过后的请求才会调用充值接口.key.p12文件是在classpath下

import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Collections;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
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.jackson2.JacksonFactory;
import com.google.api.client.util.SecurityUtils;
import com.google.api.services.androidpublisher.AndroidPublisher;
import com.google.api.services.androidpublisher.AndroidPublisherScopes;
import com.google.api.services.androidpublisher.model.ProductPurchase;
/**
 * @author weilong.wang Created on 2017/12/8
 */
@Aspect
@Component
public class GoogleSignInterceptor {
	private static final Logger logger = LoggerManage.getLogger();
	private static final String SERVICE_ACCOUNT_EMAIL = "{googlePlay.serviceAccountEmail}";
	private static final String PUBLIC_KEY = "{googlePlay.publicKey}";
	private static final String P12_Key = "/key.p12";

	@Around("execution(* xxx.xxx.xxx.vertifyOrder (..))")
	public Object preHandle(ProceedingJoinPoint proceedingJoinPoint) {
		Object[] args = proceedingJoinPoint.getArgs();
		// 获取参数
		String signature = null;
		String purchaseData = null;
		try {
			purchaseData = String.valueOf(args[0]);
			signature = String.valueOf(args[1]);
		} catch (NullPointerException e) {
			logger.error("参数为空");
		}

		// 签名校验
		String publicKey = ConfigManager.getProperty(PUBLIC_KEY);
		try {
			boolean isVertify = docheck(purchaseData, signature, publicKey);
			if (isVertify == false) {
				logger.error("签名校验失败publicKey:" + publicKey);
			}
		} catch (Exception e) {
			logger.error("签名校验失败", e);
		}
		// 调用google api二次校验
		com.alibaba.fastjson.JSONObject jsonObject;
		try {
			jsonObject = JSON.parseObject(purchaseData);
		} catch (JSONException e) {
			logger.error("purchaseData解析失败", e);
		}
		String productId = jsonObject.getString("productId");
		String packageName = jsonObject.getString("packageName");
		String purchaseToken = jsonObject.getString("purchaseToken");
		int purchaseState = jsonObject.getIntValue("purchaseState");
		if (purchaseState != 0) {
			logger.error("订单未支付!");
		}
		
		try {

			HttpTransport transport = GoogleNetHttpTransport
					.newTrustedTransport();
			PrivateKey privateKey = SecurityUtils.loadPrivateKeyFromKeyStore(
					SecurityUtils.getPkcs12KeyStore(),
					this.getClass().getResourceAsStream(P12_Key), "notasecret",
					"privatekey", "notasecret");
			GoogleCredential credential = new GoogleCredential.Builder()
					.setTransport(transport)
					.setJsonFactory(JacksonFactory.getDefaultInstance())
					.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
					.setServiceAccountScopes(Collections
							.singleton(AndroidPublisherScopes.ANDROIDPUBLISHER))
					.setServiceAccountPrivateKey(privateKey).build();
			AndroidPublisher publisher = new AndroidPublisher.Builder(transport,
					JacksonFactory.getDefaultInstance(), credential).build();
			AndroidPublisher.Purchases.Products products = publisher.purchases()
					.products();
			AndroidPublisher.Purchases.Products.Get product = products
					.get(packageName, productId, purchaseToken);

			ProductPurchase purchase = product.execute();
			if (purchase.getPurchaseState() != 0) {
				logger.error("订单未支付!");
			}
		} catch (Exception e) {
			logger.error("订单验证失败! ", e);
		}

		try {
			return proceedingJoinPoint.proceed();
		} catch (Throwable throwable) {
			logger.error("系统错误", throwable);

		}
	}

	private boolean docheck(String content, String sign, String publicKey)
			throws Exception {
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		byte[] encodedKey = Base64.getDecoder().decode(publicKey);
		PublicKey pubKey = keyFactory
				.generatePublic(new X509EncodedKeySpec(encodedKey));
		Signature signature = Signature.getInstance("SHA1WithRSA");
		signature.initVerify(pubKey);
		signature.update(content.getBytes("utf-8"));
		return signature.verify(Base64.getDecoder().decode(sign));
	}
}


前端传递的参数,仅供参考,隐私信息已去除

purchaseData={"orderId":"GPA.3330-4532-0035-01959","packageName":"{packageName}","productId":"{productName}","purchaseTime":1533737586295,"purchaseState":0,"developerPayload":"9c97bb8c-1320-4088-a9c8-6ead08325655{productName}","purchaseToken":"hcgakhnoeoiikhjicdlobfkm.AO-J1OykrKdrP6OFvIvNN3P_ZPOP8IlJSqIO95fiDYhWpB8hN6_PX23XtsEjNg8y5-pCM5AjCp8lZ_daGTQOS9HGS20xA7ev3DEqwl5_HQDnvIGTlrr_divZaA8F6zYTkF3lngn2n11j5DswMyqq84Cz6v2JlKwGWg"}&signature=fP3liv/tbrw0PUgQA/iCymOL1l56wBY8bLgqK58ktcIHGgai166ijpg9jbx3QvJ9ljgmwJca/7N/n6jzzADTy49rtpY2sNvaZDrHcwVLXXfRT9tPgMi2NgTGfRGgNw7lwXH9TkewU7rSJmeDznnuEpf8cfO24FgoUi61XZEPFrz8G83O94nQvNUgIirXQZneKmRGYWrZcomuaHmAVP3CtEchvO0AjlhDsbvZIePfkfPOAgR5PWNgwo0YpNS0TZzSErdPCDgJMxRyztisuqQnExSXnjnGtUQDSk4Ohdu4sIoeLDez8gC30yJ+g8dAtuwLw5L+yxXsEYnX7ALZk950Xg==&type=1


FAQ

  1. service account email和p12从哪里得到 都在谷歌开发者控台,仔细搜寻一下.
  2. Socket连接出现问题 99%都是翻墙问题,最好在一台国外服务器上测试,不要本地测.