最近公司要做海外版,需要接入google play.踩了不少坑,特作此文
20180809更新,增加導包,參數(shù)示例數(shù)據(jù)
20190106更新, google jar包v2版本已廢棄,api未變動,更新至v3即可
驗證方法1 rsa簽名驗證
請參考這篇
驗證方法2 請求google api驗證
需要在google developer console申請service account,具體參考這篇
我的做法 我比較diao,兩種都上了(手動滑稽)
其實是因為之前我認為publicKey在客戶端也存在,可能有風險,真正明白后才知道rsa的公匙本來就是可以可以公開的,私匙是有g(shù)oogle有,肯定是安全的,但是代碼已經(jīng)上線了,也浪費不了多少資源,算是雙重保險吧
放碼過來
maven依賴
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-androidpublisher</artifactId>
<version>v3-rev86-1.25.0</version>
</dependency>
充值接口,在這里進行充值操作
public Map<String, Object> vertifyOrder(
@RequestParam("purchaseData") String purchaseData,
@RequestParam("signature") String signature,
@RequestParam("type") int type) {
攔截器,通過后的請求才會調(diào)用充值接口.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();
// 獲取參數(shù)
String signature = null;
String purchaseData = null;
try {
purchaseData = String.valueOf(args[0]);
signature = String.valueOf(args[1]);
} catch (NullPointerException e) {
logger.error("參數(shù)為空");
}
// 簽名校驗
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);
}
// 調(diào)用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("系統(tǒng)錯誤", 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));
}
}
前端傳遞的參數(shù),僅供參考,隱私信息已去除
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
- service account email和p12從哪里得到
都在谷歌開發(fā)者控臺,仔細搜尋一下. - Socket連接出現(xiàn)問題
99%都是翻墻問題,最好在一臺國外服務(wù)器上測試,不要本地測.