Java內(nèi)購接入Apple Pay、Google play
內(nèi)購流程:
- 客戶端向服務(wù)器發(fā)起請(qǐng)求生成預(yù)訂單,服務(wù)器校驗(yàn)后生成預(yù)訂單返回客戶端。若調(diào)起支付界面后未支付,則通知服務(wù)器取消本訂單。
- 客戶端拿到預(yù)訂單號(hào)后,在玩家完成付款操作后,攜帶預(yù)訂單號(hào)請(qǐng)求支付平臺(tái),將預(yù)訂單號(hào)存儲(chǔ)在支付平臺(tái)中,并獲取支付憑證。
- 客戶端攜帶支付憑證請(qǐng)求服務(wù)器,服務(wù)器攜帶支付憑證向支付平臺(tái)請(qǐng)求
- 服務(wù)器收到支付平臺(tái)的回值后,從回值中獲取訂單的支付狀態(tài)和預(yù)訂單號(hào),并通過預(yù)訂單號(hào)定位玩家具體購買的商品信息。
- 服務(wù)器返回客戶端校驗(yàn)信息,客戶端通知支付平臺(tái)本單已結(jié)束
內(nèi)購失敗處理方式:
- 校驗(yàn)失?。ňW(wǎng)絡(luò)波動(dòng),支付平臺(tái)狀態(tài)更新異常,玩家使用重復(fù)支付憑證)則先存儲(chǔ)校驗(yàn)時(shí)需要的參數(shù),例如,支付憑證,transactionId等信息,存儲(chǔ)在數(shù)據(jù)庫和ck中
- 在后臺(tái)中創(chuàng)建定時(shí),限次數(shù)的進(jìn)行后臺(tái)校驗(yàn)。重新攜帶參數(shù)請(qǐng)求支付平臺(tái),若校驗(yàn)通過,則通過郵件形式向玩家發(fā)郵件進(jìn)行補(bǔ)償。在達(dá)到校驗(yàn)最大次數(shù)后仍未通過校驗(yàn),則此單不再參與后臺(tái)自動(dòng)校驗(yàn)。
- 未通過校驗(yàn)的玩家可以通過聯(lián)系客服進(jìn)行再次校驗(yàn)??头梢酝ㄟ^遠(yuǎn)程腳本手動(dòng)查看支付平臺(tái)返回的校驗(yàn)信息,核實(shí)后向玩家發(fā)郵件補(bǔ)償,并手動(dòng)記錄在ck中。
客戶端邏輯:
客戶端先向支付平臺(tái)請(qǐng)求正在支付的列表,若存在未完成到訂單,則像服務(wù)器發(fā)送校驗(yàn)請(qǐng)求,直到服務(wù)器向客戶端返回信息,客戶端會(huì)向支付平臺(tái)發(fā)起結(jié)束本訂單的請(qǐng)求,完成完整的一次內(nèi)購。
校驗(yàn)代碼:
Apple Pay:
public AppleResponseData getIosOrderId(String transactionId, String url) {
try {
Map<String, Object> header = new HashMap<>();
header.put("alg", "ES256");
header.put("kid", new String(Base64.getDecoder().decode(iosKid.getBytes())));
header.put("typ", "JWT");
Map<String, Object> claims = new HashMap<>();
claims.put("iss", new String(Base64.getDecoder().decode(iosIss.getBytes())));
claims.put("iat", Math.floor(System.currentTimeMillis() / 1000));
claims.put("exp", Math.floor(System.currentTimeMillis() / 1000) + 1800);
claims.put("aud", "appstoreconnect-v1");
claims.put("bid", iosBid);
//從p8文件獲取的秘鑰 此處為了方便演示直接使用的字符串,建議從p8文件中讀取,確保安全性
String key = iosPrivateKey;
PrivateKey privateKey = null;
//ES256無法使用Base64加密的秘鑰來生成jwt 必須解碼
KeyFactory kf = KeyFactory.getInstance("EC");
privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key)));
//生成jwt
String token = Jwts.builder()
.setHeader(header)
.setClaims(claims)
.signWith(SignatureAlgorithm.ES256, privateKey)
.compact();
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[]{new TrustAnyTrustManager()},
new java.security.SecureRandom());
URL console = new URL(url + transactionId);
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
conn.setSSLSocketFactory(sc.getSocketFactory());
conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
conn.setRequestMethod("GET");
conn.setRequestProperty("content-type", "text/json");
conn.setRequestProperty("Proxy-Connection", "Keep-Alive");
conn.setRequestProperty("Authorization", "Bearer "+ token);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setConnectTimeout(3000);
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = "";
StringBuilder sb = new StringBuilder();
while ((line = reader.readLine()) != null) {
sb.append(line);
}
JSONObject jsonObject = JSONObject.parseObject(sb.toString());
LogUtil.trace("ios支付回值:" + jsonObject);
String jwsTransactionBase64 = jsonObject.getString("signedTransactionInfo").split("\\.")[1];
String jwsTransactionBase = new String(Base64.getDecoder().decode(jwsTransactionBase64));
AppleResponseData appleResponseData = JSONObject.parseObject(jwsTransactionBase, new TypeReference<AppleResponseData>() {});
return appleResponseData;
} catch (Exception e) {
CommonLogUtil.error(e.getMessage(), e);
}
return null;
}
接收類:
public class AppleResponseData {
//服務(wù)器uuid
private String appAccountToken;
//應(yīng)用包名
private String bundleId;
private String currency;
//沙盒或者正式環(huán)境
private String environment;
//購買時(shí)間
private long originalPurchaseDate;
//產(chǎn)品id
private String productId;
//唯一標(biāo)識(shí)
private String transactionId;
}
官方文檔: https://developer.apple.com/documentation/appstoreserverapi/get_transaction_history
需要參數(shù):文章來源:http://www.zghlxwxcb.cn/news/detail-853812.html
- transactionId:支付完成后客戶端從IOS平臺(tái)獲取的支付憑證。
- url:服務(wù)器校驗(yàn)地址,
沙盒:https://api.storekitsandbox.itunes.apple.com/inApps/v1/transactions/ (+ transactionId)
正式:https://api.storekit.itunes.apple.com/inApps/v1/history/ (+ transactionId) - kid:密鑰id
- iss:issuer ID
- bid:應(yīng)用名稱,例如 com.company.production
- privateKey:已授權(quán)賬號(hào)在ios官網(wǎng)下載的p8文件中的內(nèi)容
Google Play:
public ProductPurchase googlePlayPayRequest(String productId, String purchaseToken,
String packageName) {
try {
openTemProxy();
List<String> scopes = new ArrayList<>();
scopes.add(AndroidPublisherScopes.ANDROIDPUBLISHER);
StringInputStream stringInputStream = new StringInputStream(new String(Base64.getDecoder().decode(googleVerifyJson)));
GoogleCredential credential = GoogleCredential.fromStream(stringInputStream)
.createScoped(scopes);
//設(shè)置過期時(shí)間
credential.setExpirationTimeMilliseconds(3000L);
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = new JacksonFactory();
AndroidPublisher publisher = new AndroidPublisher.Builder(httpTransport, jsonFactory,
credential).build();
AndroidPublisher.Purchases purchases = publisher.purchases();
final AndroidPublisher.Purchases.Products.Get request = purchases.products()
.get(packageName, productId, purchaseToken);
return request.execute();
} catch (Exception e) {
LogUtil.info(e.getMessage(), e);
} finally {
Properties systemProperties = System.getProperties();
if(openProxy){
systemProperties.clear();
}
}
return null;
}
/**
* 開啟代理
*/
public void openTemProxy() {
if(true){
//代理端口
try {
Properties systemProperties = System.getProperties();
systemProperties.setProperty("http.proxyHost", "127.0.0.1");
systemProperties.setProperty("http.proxyPort", "7890");
systemProperties.setProperty("https.proxyHost", "127.0.0.1");
systemProperties.setProperty("https.proxyPort", "7890");
systemProperties.setProperty("socksProxyHost", "127.0.0.1");
systemProperties.setProperty("socksProxyPort", "7890");
systemProperties.setProperty("http.nonProxyHosts", "localhost");
systemProperties.setProperty("https.nonProxyHosts", "localhost");
} catch (Exception e) {
CommonLogUtil.error(e.getMessage(), e);
}
}
}
maven:
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-androidpublisher</artifactId>
<version>v3-rev24-1.24.1</version>
</dependency>
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-oauth2-http</artifactId>
<version>1.3.0</version>
</dependency>
官方文檔: https://developers.google.cn/android-publisher/api-ref/rest/v3/purchases.products?skip_cache=true&hl=zh-cn
需要參數(shù):
- productId: 產(chǎn)品id
- purchaseToken: 購買憑證
- packageName: 項(xiàng)目名稱
- verifyJson: google后臺(tái)獲取授權(quán)賬號(hào)json文件
google授權(quán)文檔: https://www.quicksdk.com/doc-516.html文章來源地址http://www.zghlxwxcb.cn/news/detail-853812.html
到了這里,關(guān)于Java接入內(nèi)購 Apple Pay、Google Play的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!