Java接入内购 Apple Pay、Google Play

Java内购接入Apple Pay、Google play

内购流程:

  1. 客户端向服务器发起请求生成预订单,服务器校验后生成预订单返回客户端。若调起支付界面后未支付,则通知服务器取消本订单。
  2. 客户端拿到预订单号后,在玩家完成付款操作后,携带预订单号请求支付平台,将预订单号存储在支付平台中,并获取支付凭证。
  3. 客户端携带支付凭证请求服务器,服务器携带支付凭证向支付平台请求
  4. 服务器收到支付平台的回值后,从回值中获取订单的支付状态和预订单号,并通过预订单号定位玩家具体购买的商品信息。
  5. 服务器返回客户端校验信息,客户端通知支付平台本单已结束

内购失败处理方式:

  1. 校验失败(网络波动,支付平台状态更新异常,玩家使用重复支付凭证)则先存储校验时需要的参数,例如,支付凭证,transactionId等信息,存储在数据库和ck中
  2. 在后台中创建定时,限次数的进行后台校验。重新携带参数请求支付平台,若校验通过,则通过邮件形式向玩家发邮件进行补偿。在达到校验最大次数后仍未通过校验,则此单不再参与后台自动校验。
  3. 未通过校验的玩家可以通过联系客服进行再次校验。客服可以通过远程脚本手动查看支付平台返回的校验信息,核实后向玩家发邮件补偿,并手动记录在ck中。

客户端逻辑:

客户端先向支付平台请求正在支付的列表,若存在未完成到订单,则像服务器发送校验请求,直到服务器向客户端返回信息,客户端会向支付平台发起结束本订单的请求,完成完整的一次内购。

校验代码:

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 {

  //服务器uuid
  private String appAccountToken;

  //应用包名
  private String bundleId;

  private String currency;

  //沙盒或者正式环境
  private String environment;

  //购买时间
  private long originalPurchaseDate;

  //产品id
  private String productId;

  //唯一标识
  private String transactionId;
}

官方文档: https://developer.apple.com/documentation/appstoreserverapi/get_transaction_history

需要参数:

  1. transactionId:支付完成后客户端从IOS平台获取的支付凭证。
  2. url:服务器校验地址,
    沙盒:https://api.storekitsandbox.itunes.apple.com/inApps/v1/transactions/ (+ transactionId)
    正式:https://api.storekit.itunes.apple.com/inApps/v1/history/ (+ transactionId)
  3. kid:密钥id
  4. iss:issuer ID
  5. bid:应用名称,例如 com.company.production
  6. privateKey:已授权账号在ios官网下载的p8文件中的内容
    在这里插入图片描述
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);
      //设置过期时间
      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

需要参数:

  1. productId: 产品id
  2. purchaseToken: 购买凭证
  3. packageName: 项目名称
  4. verifyJson: google后台获取授权账号json文件

google授权文档: https://www.quicksdk.com/doc-516.html

  • 31
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值