本页目录

IAP介绍

IAP支付全称In-App purchase,是指苹果App Store的应用内购买,是苹果为App内购买虚拟商品或服务提供的一套交易系统。

IAP参考连接

参考别人的App Store Connect配置信息:https://www.pianshen.com/article/7151368564/
苹果抽成表一览(需登录):https://appstoreconnect.apple.com/apps/pricingmatrix

接入IAP快速导航:

  1. 认证协议(签订银行信息)
  2. 设定商品价格
  3. 上线配置
  4. 注册沙箱环境
  5. java编码 tohashmap

准备工作 认证协议(签订银行信息)

首先进入App Store Connect 点击 协议、税务和银行业务

填写基本的银行信息。然后再填写一些基本信息(强制要求的填写,没强制的没必要!!!)

提交等待24小时,成功,回到App信息就能看到成功的信息了

设定商品价格

App Connect 进入自己的APP,然后点击管理 (必须第一步的“认证协议”) 就能看到添加App 内购项目

注意:这里需要根据自己情况选择内购项目的类型

创建相应的信息即可,下图是你设置文本演示的对应支付时的展示位置

商品设定完成!前端可能需要这个产品ID,建议设置为 com.公司名.项目名.物品ID

如:com.hefeixunliao.zhenliao.12yuan

productId 上线设置

上述操作提交完成后,切记检查App内购项目的状态:元数据丢失的 内购项目可以进行沙箱测试,但上线不可用,上线使用必须是 准备提交状态

苹果说明

查看说明: https://help.apple.com/app-store-connect/#/dev1986a0e5c

真实配置

点击元数据丢失的内容

填写本地化内容,也就是充值的时候,显示的充值信息。以及此内购项目的在那里展示的,如 充值页面的截图。就可提交审核了。等待审核!

上线务必保障勾选 被苹果审核通过的内购项目!!!

配置完成后,你下个APP版本就拥有了 App内购项目了!

注册自己的沙箱账号:

添加沙箱账号

地图选择 中国大陆,否则会影响测试!电子邮箱不可与正规Apple 邮箱账号一致!

完成即可登录了。在APP内部测试的时候,会提示你是沙箱环境,建议使用沙箱账户!!!

如果需要更换自己的测试沙箱账户,请在Iphone – 设置 – App Store – 沙盒账户 点击可以退出登录。

Java编码 大致编码如下,具体会随着苹果业务变动,传递参数会发生相应变化

苹果支付业务思维导图

基础 Domain

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.io.Serializable;/*** @Author: zanglikun* @Date: 2022/5/7 15:23* @Description: 拿着前端给的长字符串 去向Apple校验,Apple服务器响应的对象* Desc from Apple Web:https://developer.apple.com/documentation/appstorereceipts/responsebody*/
@ApiModel("向Apple校验的所有返回结果")
@Data
public class AppleResponse implements Serializable {@ApiModelProperty("交易环境:可能的值:Sandbox, Production")private String environment;@ApiModelProperty("交易环境:可能的值:Sandbox, Production")private AppleReceipt receipt;@ApiModelProperty("如果收据有效,或者0如果有错误,则返回状态代码。状态码反映了整个应用收据的状态。")private String status;
}

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.io.Serializable;/*** @Author: zanglikun* @Date: 2022/5/7 15:25* @Description: Desc from Apple Web:https://developer.apple.com/documentation/appstorereceipts/responsebody/receipt*/
@ApiModel(value = "苹果响应的核心数据,反应了一些重要的数据")
@Data
public class AppleReceipt implements Serializable {@ApiModelProperty(value = "生成的收据类型。该值对应于进行应用程序或 VPP 购买的环境。\n" +"可能的值:Production, ProductionVPP, ProductionSandbox, ProductionVPPSandbox")private String receipt_type;@ApiModelProperty(value = "见。app_item_id")private Long adam_id;@ApiModelProperty(value = "由 App Store Connect 生成并由 App Store 用于唯一标识所购买的应用程序。仅在生产中为应用分配此标识符。将此值视为 64 位长整数。")private Long app_item_id;@ApiModelProperty(value = "收据所属应用的捆绑包标识符。您在 App Store Connect 上提供此字符串。这对应于应用程序文件中的值。CFBundleIdentifierInfo.plist")private String bundle_id;@ApiModelProperty(value = "应用程序的版本号。应用程序的版本号对应于. 在生产中,此值是设备上基于. 在沙盒中,该值始终为。CFBundleVersionCFBundleShortVersionStringInfo.plistreceipt_creation_date_ms\"1.0\"")private String application_version;@ApiModelProperty(value = "应用下载交易的唯一标识符。")private Long download_id;@ApiModelProperty(value = "一个任意数字,用于标识您的应用程序的修订版。在沙箱中,此键的值为“0”。")private Long version_external_identifier;@ApiModelProperty(value = "App Store 生成收据的时间,采用类似于 ISO 8601 的日期时间格式。")private String receipt_creation_date;@ApiModelProperty(value = "App Store 生成收据的时间,采用 UNIX 纪元时间格式,以毫秒为单位。使用此时间格式处理日期。这个值不会改变。")private String receipt_creation_date_ms;@ApiModelProperty(value = "App Store 生成收据的时间,在太平洋时区。")private String receipt_creation_date_pst;@ApiModelProperty(value = "处理对端点的请求并生成响应的时间,采用类似于 ISO 8601 的日期时间格式。verifyReceipt")private String request_date;@ApiModelProperty(value = "处理对端点的请求并生成响应的时间,采用 UNIX 纪元时间格式,以毫秒为单位。使用此时间格式处理日期。verifyReceipt")private String request_date_ms;@ApiModelProperty(value = "在太平洋时区处理对端点的请求并生成响应的时间。verifyReceipt")private String request_date_pst;@ApiModelProperty(value = "原始应用购买的时间,采用类似于 ISO 8601 的日期时间格式。")private String original_purchase_date;@ApiModelProperty(value = "原始应用购买的时间,采用 UNIX 纪元时间格式,以毫秒为单位。使用此时间格式处理日期。")private String original_purchase_date_ms;@ApiModelProperty(value = "原始应用购买时间,太平洋时区。")private String original_purchase_date_pst;@ApiModelProperty(value = "用户最初购买的应用版本。该值不变,对应原始购买文件中(iOS 中)或String(macOS 中)的值。在沙盒环境中,该值始终为.CFBundleVersionCFBundleShortVersionInfo.plist\"1.0\"")private String original_application_version;@ApiModelProperty(value = "包含所有应用内购买交易的应用内购买收据字段的数组。")private AppleOrder[] in_app;
}

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.io.Serializable;/*** @Author: zanglikun* @Date: 2022/5/7 14:43* @Description: 苹果回调的订单对象,因为苹果回调字段 in_app给了很多 订单信息,所以我们以AppleOrder数组接,方便Java处理* Desc From Apple Web:https://developer.apple.com/documentation/appstorereceipts/responsebody/receipt/in_app*/
@ApiModel(value = "苹果返回的所有订单信息 In_App")
@Data
public class AppleOrder implements Serializable {@ApiModelProperty(value = "购买的消耗品数量。此值对应于SKPayment存储在交易的支付属性中的对象的数量属性。“1”除非使用可变付款进行修改,否则该值通常是不变的。最大值为 10。")private String quantity;@ApiModelProperty(value = "购买的产品的唯一标识符。您在 App Store Connect 中创建产品时提供此值,它对应于存储在交易的支付属性中的对象的属性。productIdentifierSKPayment")private String product_id;@ApiModelProperty(value = "交易的唯一标识符,例如购买、恢复或续订。有关更多信息,请参阅")private String transaction_id;@ApiModelProperty(value = "原始购买的交易标识符。有关更多信息,请参阅。original_transaction_id")private String original_transaction_id;@ApiModelProperty(value = "App Store 向用户帐户收取购买或恢复产品费用的时间,或 App Store 向用户帐户收取订阅购买或续订费用的时间,采用类似于 ISO 8601 的日期时间格式。")private String purchase_date;@ApiModelProperty(value = "对于消耗性、非消耗性和非续订订阅产品,App Store 向用户帐户收取购买或恢复产品的时间,采用 UNIX 纪元时间格式,以毫秒为单位。对于自动续订订阅,App Store 向用户帐户收取订阅购买或过期后续订的时间,采用 UNIX 纪元时间格式,以毫秒为单位。使用此时间格式处理日期。")private String purchase_date_ms;@ApiModelProperty(value = "在太平洋时区,App Store 向用户帐户收取购买或恢复产品费用的时间,或 App Store 向用户帐户收取订阅购买或续订费用的时间。")private String purchase_date_pst;@ApiModelProperty(value = "原始应用内购买的时间,采用类似于 ISO 8601 的日期时间格式。")private String original_purchase_date;@ApiModelProperty(value = "原始应用内购买的时间,采用 UNIX 纪元时间格式,以毫秒为单位。对于自动续订订阅,此值表示订阅的初始购买日期。原始购买日期适用于所有产品类型,并且在相同产品 ID 的所有交易中保持相同。此值对应于 StoreKit 中原始交易的属性。使用此时间格式处理日期。transactionDate")private String original_purchase_date_ms;@ApiModelProperty(value = "原始应用内购买的时间,在太平洋时区。")private String original_purchase_date_pst;@ApiModelProperty(value = "指示订阅是否处于免费试用期。有关更多信息,请参阅。is_trial_period")private String is_trial_period;@ApiModelProperty(value = "狗日的苹果没说:沙箱环境是:PURCHASED")private String in_app_ownership_type;
}

import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateTime;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;/*** @author : zanglikun* @date : 2021/11/18 9:40* @Version: 1.0* @Desc : 苹果支付 参考地址:https://www.cnblogs.com/shoshana-kong/p/10991753.html* sendHttpsCoon 方法里面包含了增加的业务逻辑* 本类引入多种的Json序列化工具,选择了Hutool*/
@Slf4j
@Controller
@RequestMapping("applePay")
public class ApplePayController {//购买凭证验证地址private static final String certificateUrl = "https://buy.itunes.apple.com/verifyReceipt";//测试的购买凭证验证地址private static final String certificateUrlTest = "https://sandbox.itunes.apple.com/verifyReceipt";/*** 重写X509TrustManager*/private static TrustManager myX509TrustManager = new X509TrustManager() {@Overridepublic X509Certificate[] getAcceptedIssuers() {return null;}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}};/*** 接收自己APP客户端发过来的购买凭证** @param userId    用户ID* @param receipt   前端支付成功后:苹果给前端的一长串字符串* @param chooseEnv false代表苹果沙箱环境,true代表真实支付环境*/@PostMapping("/setIapCertificate")@ApiImplicitParams({@ApiImplicitParam(name = "userId", value = "用户Id"), @ApiImplicitParam(name = "receipt", value = "苹果传递前端支付成功的值"), @ApiImplicitParam(name = "chooseEnv", value = "是否是真实环境,布尔值")})@ResponseBodypublic String setIapCertificate(String userId, String receipt, boolean chooseEnv) {// log.info("IOS端发送的购买凭证。数据有 userId = {},receipt = {},chooseEnv = {}",userId,receipt,chooseEnv);if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(receipt)) {return "后台发送道具失败,用户ID 或者 receipt为空";}String url = null;url = chooseEnv == true ? certificateUrl : certificateUrlTest;final String certificateCode = receipt;if (StringUtils.isNotEmpty(certificateCode)) {String s = sendHttpsCoon(url, certificateCode, userId);if ("支付成功".equals(s)) {return "后台发送道具成功了,返回值按需自定义即可";} else {return "后台发送道具失败了!";}} else {return "后台发送道具失败!传参receipt 为空!";}}/*** 苹果沙箱账号密码:Xunliaoceshi002* 发送请求 向苹果校验支付请求是否有效:本方法由认证方法进行调用** @param url     支付的环境校验,不同Url代表 真实支付或苹果沙箱环境* @param receipt 接口传递的 receipt* @return 苹果响应的请求结果*/private String sendHttpsCoon(String url, String receipt, String userId) {if (url.isEmpty() || receipt.isEmpty() || userId.isEmpty()) {return "调用传参错误!";}String line = null;// todo 理论应该锁UserId,因为后台只能允许一个线程执行给玩家发送道具!try {// 设置SSLContextSSLContext ssl = SSLContext.getInstance("SSL");ssl.init(null, new TrustManager[]{myX509TrustManager}, null);// 打开连接HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection();// 设置套接工厂conn.setSSLSocketFactory(ssl.getSocketFactory());// 加入数据conn.setRequestMethod("POST");conn.setDoOutput(true);conn.setRequestProperty("Content-type", "application/json");// JSONObject obj = new JSONObject();JSONObject obj = new JSONObject();obj.set("receipt-data", receipt);// 发送请求BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());buffOutStr.write(obj.toString().getBytes());buffOutStr.flush();buffOutStr.close();// 获取接收响应的对象readerBufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));// 转换苹果响应的所有结果!StringBuffer sb = new StringBuffer();while ((line = reader.readLine()) != null) {sb.append(line);}// 处理苹果响应的结果AppleResponse appleResponse = JSONUtil.toBean(sb.toString(), AppleResponse.class);log.info("用户Id{},回调获取到了{}", userId, appleResponse.toString());// 苹果说了,只有订单状态是0才算成功,其他均是错误,可查:https://developer.apple.com/documentation/appstorereceipts/statusif (StringUtils.equalsAny(appleResponse.getStatus(), "0")) {// 苹果给的订单号AppleOrder[] in_app = appleResponse.getReceipt().getIn_app();for (AppleOrder appleOrder : in_app) {// 从订单对象获取订单号String transaction_id = appleOrder.getTransaction_id();String original_purchase_date_ms = appleOrder.getOriginal_purchase_date_ms();System.out.println(original_purchase_date_ms);log.info("用户下单时间:{}",new DateTime(Long.valueOf(original_purchase_date_ms)+16*60*60*1000));// todo 校验订单号没有发放过道具if (true) {String quantity = appleOrder.getQuantity();// 获取用户购买的产品Id,String product_id = appleOrder.getProduct_id();switch (product_id) {case "com.hefeixunliao.zhenliao6yuan": {break;}case "com.hefeixunliao.zhenliao999yuan": {break;}}log.info("{}发送奖励成功喽!",userId);}}return "支付成功";} else {log.info("用户ID:{},取消支付,响应码是:{}", userId, appleResponse.getStatus());}} catch (Exception e) {log.error("苹果回调过程中出现异常:{}", e.getMessage());return "有异常,停止交易!";}return "苹果回调处理失败!";}/*** 注意:下面代码跟苹果支付业务无关。* 这里的code 是前端请求苹果获取到的参数* 苹果给前端的一个密钥(如果我们通过base64解密后,可获得signature、purchase-info、environment、pod、signing-status)* 这个密钥用于告诉我们自己Java服务器 想苹果服务器校验订单是否成功的参数*/@Testpublic void decode() {String code = "ewoJInNpZ25hdHVyZSIgPSAiQXhmVWRiYUx5T2I5bllOM3hINmQzMnBaOHI2THdmV3ZmZ1NKN1o2QTM4dEY2SjNyUTZoRVZqQ3Rra01wMnhmM1pwWnFQRmw3ZlRIdDVxNVpKZUF6UWh4NWQ1djJrR01uM3NKb3ZBWXNuWENxY3VqclBWU3A5WTFYUTZjeTlvbVNORWNYVWt0L1dkQXhsRmN6WDRZMTJzcktsMDc3WHJIdk5JMDd0VTZXajgzbVdDNE1HZmF0c2E2UEo1RG5sT2lEOG96RlJ6a0NIQ3Y3bncvRm80dnFCaFpZRmlQSDZzeW1uN2lUQlhTcXlTdlJOTGJXLytUWktKZngxR1dRV3BWdmJ5M0RtV3l4OTRaYkxGRllNODE0aTB2a1lnWDdPdVUwQWprTVFKOEJhNnJGc1hER0hYY2FCdnVhZ1NGak9iMWdJclZ2MDdmbTlBV3ltNE5KT0dVSHR0Z0FBQVdBTUlJRmZEQ0NCR1NnQXdJQkFnSUlEdXRYaCtlZUNZMHdEUVlKS29aSWh2Y05BUUVGQlFBd2daWXhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFLREFwQmNIQnNaU0JKYm1NdU1Td3dLZ1lEVlFRTERDTkJjSEJzWlNCWGIzSnNaSGRwWkdVZ1JHVjJaV3h2Y0dWeUlGSmxiR0YwYVc5dWN6RkVNRUlHQTFVRUF3dzdRWEJ3YkdVZ1YyOXliR1IzYVdSbElFUmxkbVZzYjNCbGNpQlNaV3hoZEdsdmJuTWdRMlZ5ZEdsbWFXTmhkR2x2YmlCQmRYUm9iM0pwZEhrd0hoY05NVFV4TVRFek1ESXhOVEE1V2hjTk1qTXdNakEzTWpFME9EUTNXakNCaVRFM01EVUdBMVVFQXd3dVRXRmpJRUZ3Y0NCVGRHOXlaU0JoYm1RZ2FWUjFibVZ6SUZOMGIzSmxJRkpsWTJWcGNIUWdVMmxuYm1sdVp6RXNNQ29HQTFVRUN3d2pRWEJ3YkdVZ1YyOXliR1IzYVdSbElFUmxkbVZzYjNCbGNpQlNaV3hoZEdsdmJuTXhFekFSQmdOVkJBb01Da0Z3Y0d4bElFbHVZeTR4Q3pBSkJnTlZCQVlUQWxWVE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcGMrQi9TV2lnVnZXaCswajJqTWNqdUlqd0tYRUpzczl4cC9zU2cxVmh2K2tBdGVYeWpsVWJYMS9zbFFZbmNRc1VuR09aSHVDem9tNlNkWUk1YlNJY2M4L1cwWXV4c1FkdUFPcFdLSUVQaUY0MWR1MzBJNFNqWU5NV3lwb041UEM4cjBleE5LaERFcFlVcXNTNCszZEg1Z1ZrRFV0d3N3U3lvMUlnZmRZZUZScjZJd3hOaDlLQmd4SFZQTTNrTGl5a29sOVg2U0ZTdUhBbk9DNnBMdUNsMlAwSzVQQi9UNXZ5c0gxUEttUFVockFKUXAyRHQ3K21mNy93bXYxVzE2c2MxRkpDRmFKekVPUXpJNkJBdENnbDdaY3NhRnBhWWVRRUdnbUpqbTRIUkJ6c0FwZHhYUFEzM1k3MkMzWmlCN2o3QWZQNG83UTAvb21WWUh2NGdOSkl3SURBUUFCbzRJQjF6Q0NBZE13UHdZSUt3WUJCUVVIQVFFRU16QXhNQzhHQ0NzR0FRVUZCekFCaGlOb2RIUndPaTh2YjJOemNDNWhjSEJzWlM1amIyMHZiMk56Y0RBekxYZDNaSEl3TkRBZEJnTlZIUTRFRmdRVWthU2MvTVIydDUrZ2l2Uk45WTgyWGUwckJJVXdEQVlEVlIwVEFRSC9CQUl3QURBZkJnTlZIU01FR0RBV2dCU0lKeGNKcWJZWVlJdnM2N3IyUjFuRlVsU2p0ekNDQVI0R0ExVWRJQVNDQVJVd2dnRVJNSUlCRFFZS0tvWklodmRqWkFVR0FUQ0IvakNCd3dZSUt3WUJCUVVIQWdJd2diWU1nYk5TWld4cFlXNWpaU0J2YmlCMGFHbHpJR05sY25ScFptbGpZWFJsSUdKNUlHRnVlU0J3WVhKMGVTQmhjM04xYldWeklHRmpZMlZ3ZEdGdVkyVWdiMllnZEdobElIUm9aVzRnWVhCd2JHbGpZV0pzWlNCemRHRnVaR0Z5WkNCMFpYSnRjeUJoYm1RZ1kyOXVaR2wwYVc5dWN5QnZaaUIxYzJVc0lHTmxjblJwWm1sallYUmxJSEJ2YkdsamVTQmhibVFnWTJWeWRHbG1hV05oZEdsdmJpQndjbUZqZEdsalpTQnpkR0YwWlcxbGJuUnpMakEyQmdnckJnRUZCUWNDQVJZcWFIUjBjRG92TDNkM2R5NWhjSEJzWlM1amIyMHZZMlZ5ZEdsbWFXTmhkR1ZoZFhSb2IzSnBkSGt2TUE0R0ExVWREd0VCL3dRRUF3SUhnREFRQmdvcWhraUc5Mk5rQmdzQkJBSUZBREFOQmdrcWhraUc5dzBCQVFVRkFBT0NBUUVBRGFZYjB5NDk0MXNyQjI1Q2xtelQ2SXhETUlKZjRGelJqYjY5RDcwYS9DV1MyNHlGdzRCWjMrUGkxeTRGRkt3TjI3YTQvdncxTG56THJSZHJqbjhmNUhlNXNXZVZ0Qk5lcGhtR2R2aGFJSlhuWTR3UGMvem83Y1lmcnBuNFpVaGNvT0FvT3NBUU55MjVvQVE1SDNPNXlBWDk4dDUvR2lvcWJpc0IvS0FnWE5ucmZTZW1NL2oxbU9DK1JOdXhUR2Y4YmdwUHllSUdxTktYODZlT2ExR2lXb1IxWmRFV0JHTGp3Vi8xQ0tuUGFObVNBTW5CakxQNGpRQmt1bGhnd0h5dmozWEthYmxiS3RZZGFHNllRdlZNcHpjWm04dzdISG9aUS9PamJiOUlZQVlNTnBJcjdONFl0UkhhTFNQUWp2eWdhWndYRzU2QWV6bEhSVEJoTDhjVHFBPT0iOwoJInB1cmNoYXNlLWluZm8iID0gImV3b0pJbTl5YVdkcGJtRnNMWEIxY21Ob1lYTmxMV1JoZEdVdGNITjBJaUE5SUNJeU1ESXhMVEV4TFRJMklESXlPalU1T2pJd0lFRnRaWEpwWTJFdlRHOXpYMEZ1WjJWc1pYTWlPd29KSW5WdWFYRjFaUzFwWkdWdWRHbG1hV1Z5SWlBOUlDSTROelU1WmpNMVlUTTNNMk0wTVRabU5qazRPVFJrWkRRd05XRTFOemhoTURoalpqSTVOMlkwSWpzS0NTSnZjbWxuYVc1aGJDMTBjbUZ1YzJGamRHbHZiaTFwWkNJZ1BTQWlNVEF3TURBd01Ea3hPVE13TWpFeU5DSTdDZ2tpWW5aeWN5SWdQU0FpTVRBd0lqc0tDU0owY21GdWMyRmpkR2x2YmkxcFpDSWdQU0FpTVRBd01EQXdNRGt4T1RNd01qRXlOQ0k3Q2draWNYVmhiblJwZEhraUlEMGdJakVpT3dvSkltbHVMV0Z3Y0MxdmQyNWxjbk5vYVhBdGRIbHdaU0lnUFNBaVVGVlNRMGhCVTBWRUlqc0tDU0p2Y21sbmFXNWhiQzF3ZFhKamFHRnpaUzFrWVhSbExXMXpJaUE5SUNJeE5qTTNPVGsyTXpZd01qYzNJanNLQ1NKMWJtbHhkV1V0ZG1WdVpHOXlMV2xrWlc1MGFXWnBaWElpSUQwZ0lqaEJSRVJHUlRjMUxURkVRVVl0TkRVek1TMDRNakV3TFVKRE9UZzNSa1l3TlRrNU9DSTdDZ2tpY0hKdlpIVmpkQzFwWkNJZ1BTQWlZMjl0TG1obFptVnBlSFZ1YkdsaGJ5NTZhR1Z1YkdsaGJ6TXdlWFZoYmlJN0Nna2lhWFJsYlMxcFpDSWdQU0FpTVRVNU5qWXpNRGcyT0NJN0Nna2lZbWxrSWlBOUlDSmpiMjB1YUdWbVpXbDRkVzVzYVdGdkxucG9aVzVzYVdGdklqc0tDU0pwY3kxcGJpMXBiblJ5YnkxdlptWmxjaTF3WlhKcGIyUWlJRDBnSW1aaGJITmxJanNLQ1NKd2RYSmphR0Z6WlMxa1lYUmxMVzF6SWlBOUlDSXhOak0zT1RrMk16WXdNamMzSWpzS0NTSndkWEpqYUdGelpTMWtZWFJsSWlBOUlDSXlNREl4TFRFeExUSTNJREEyT2pVNU9qSXdJRVYwWXk5SFRWUWlPd29KSW1sekxYUnlhV0ZzTFhCbGNtbHZaQ0lnUFNBaVptRnNjMlVpT3dvSkluQjFjbU5vWVhObExXUmhkR1V0Y0hOMElpQTlJQ0l5TURJeExURXhMVEkySURJeU9qVTVPakl3SUVGdFpYSnBZMkV2VEc5elgwRnVaMlZzWlhNaU93b0pJbTl5YVdkcGJtRnNMWEIxY21Ob1lYTmxMV1JoZEdVaUlEMGdJakl3TWpFdE1URXRNamNnTURZNk5UazZNakFnUlhSakwwZE5WQ0k3Q24wPSI7CgkiZW52aXJvbm1lbnQiID0gIlNhbmRib3giOwoJInBvZCIgPSAiMTAwIjsKCSJzaWduaW5nLXN0YXR1cyIgPSAiMCI7Cn0=";byte[] decode = Base64.decode(code.getBytes());System.out.println(new String(decode));}
}

先看下每个字段的含义,在看输出的结果

字段 类型 描述
transaction_id integer 交易号
original_transaction_id integer 原始交易号
product_id string 商品标识符
quantity integer 数量
purchase_date string 购买日期
original_purchase_date string 原始购买日期
purchase_date_ms integer 购买日期(ms)
original_purchase_date_ms integer 原始购买日期(ms)
purchase_date_pst string 购买日期(pst)
original_purchase_date_pst string 原始购买日期(pst)
cancellation_date string 取消购买的日期

返回的结果是(我已经Json格式化了):

{"data":{"receipt":{"original_purchase_date_pst":"2021-11-26 18:24:16 America/Los_Angeles","purchase_date_ms":"1637979856000","unique_identifier":"8759f35a373c416f69894dd405a578a08cf297f4","original_transaction_id":"1000000919276978","bvrs":"100","transaction_id":"1000000919276978","quantity":"1","in_app_ownership_type":"PURCHASED","unique_vendor_identifier":"8ADDFE75-1DAF-4531-8210-BC987FF05998","item_id":"1596630862","original_purchase_date":"2021-11-27 02:24:16 Etc/GMT","is_in_intro_offer_period":"false","product_id":"com.hefeixunliao.zhenliao12yuan","purchase_date":"2021-11-27 02:24:16 Etc/GMT","is_trial_period":"false","purchase_date_pst":"2021-11-26 18:24:16 America/Los_Angeles","bid":"com.hefeixunliao.zhenliao","original_purchase_date_ms":"1637979856000"},"status":0},"statusCode":200,"header":{"Date":"Sat, 27 Nov 2021 02:25:22 GMT","Keep-Alive":"timeout=60","Content-Length":"794","Connection":"keep-alive","Content-Type":"text/plain;charset=UTF-8","Vary":"Origin, Access-Control-Request-Method, Access-Control-Request-Headers"},"errMsg":"request:ok","cookies":[]
}

代码编写完成,去重新打包吧。搞完切记回头看下 #5

特殊说明: 以上文章,均是我实际操作,写出来的笔记资料,不会盗用别人文章!烦请各位,请勿直接盗用!转载记得标注来源!
收_心

永久会员

打赏 收藏 海报 链接

Java接入苹果支付 – IAP支付 – IOS应用内支付- 完整版相关推荐

  1. php手机发卡,PHP最新金发卡企业级发卡平台整站源码(自适应手机端) 支付通道齐全 运营级自动发卡完整版源码 发卡网源码...

    [温馨提示]源码包解压密码:www.youhutong.com 资源描述 PHP最新金发卡企业级发卡平台整站源码(自适应手机端) 支付通道齐全 运营级自动发卡完整版源码 发卡网源码 安装教程: 环境p ...

  2. ios应用内支付过程(使用苹果原生支付方式

    因为ios政策问题,如果开发者需要在ios客户端中加入购买(虚拟货币)项目,需要使用ios应用内付费IAP这种方式,这也是很多苹果APP不和安卓共通的原因.因为苹果需要抽取%30的利益,详细原因就不赘 ...

  3. iOS应用内支付(IAP)的那些坑

    我们在今年春节后上线了新的在线智能题库:猿题库.猿题库现在推出了公务员考试行测和申论2个产品,均包括web, iOS和Android三个平台.这次我们尝试做一个收费的产品,所以在iOS端集成了应用内支 ...

  4. iOS应用内支付(IAP)的注意事项

    来源:http://blog.csdn.net/xinruiios/article/details/9289573 IAP的全称是In-App Purchase,应用内付费.这种业务模式允许用户免费下 ...

  5. ios 应用内支付(In-App Purchase,沙盒测试,后台验证)

    1.苹果iTunes Connect内购产品信息录入. 1)创建app内购买项目(Create New),选择类型: 1.消耗型项目 对于消耗型 App 内购买项目,用户每次下载时都必须进行购买.一次 ...

  6. 【Java学习日志3.31】经典扫雷带界面完整版

    图形用户界面(GUI)的实现是用Java自带的两个包awt和swing, 扫雷的实现用了四个类 一. ClearMineMap 用于存储底层的数据 运行他可以在控制台显示布雷的数据 package c ...

  7. IOS 应用内支付(IAP)接口使用说明

    群里看到一个关于ios支付回调问题如下 问题: 箭头函数的回调没有执行 解决办法: 不要着急改成箭头函数, 先按官网复制代码跑通流程 为什么用箭头函数有这个bug呢? 文档说了 : 服务端要做好防重校 ...

  8. php苹果h5微信支付白屏,iOS 微信h5支付 返回APP显示支付结果

    作者:QuinceyYang (YangQing) 一.解决UIWebView页面不能调起微信支付 - (BOOL)webView:(UIWebView *)webView shouldStartLo ...

  9. [iOS]应用内支付(内购)的个人开发过程及坑!

    本文基于XcodeVersion 7.3 (7D175)版本,手机是iPhone 6,9.3系统. 一. 创建测试App 首先你需要登录 App的ItunesConnection,你会看到如下界面 简 ...

最新文章

  1. leetcode-2 两数相加
  2. LineMod模板匹配算法的原理与实现 (原理及公式)
  3. 点云数据格式 数据集笔记
  4. Springboot项目因为kackson版本问题启动报错解决方案
  5. 机器人聊天软件c#_使用python3.7配置开发钉钉群自定义机器人(2020年新版攻略)
  6. 使用imbalanced-learn处理数据不均衡问题
  7. 讲一讲应用服务的新鲜事儿
  8. VS2008下最新X264(svn 2009.9)编译不过的解决办法
  9. 关于 vmware虚拟机的一些问题及解决办法备忘
  10. 前端CSS基础——表单元素单选框的美化
  11. php阿里云短信验证码
  12. 计算机维修管理平台软件,美萍电脑行业管理软件(电脑业务管理系统、电脑维修管理系统、组装业务、电脑装机管理软件)--管理软件,美萍是专家!...
  13. 【语音识别】基于HMM实现中文语音识别含Matlab源码
  14. location.hostnbsp;与nbsp;locat…
  15. 周易六十四卦——风火家人卦
  16. 5G商用价值到底在哪里,可以赋予哪些能力?
  17. 一、欢迎来到趣味编程的世界
  18. 新时代区块链研究院 | 区块链人才需求激增200%,薪资已赶超互联网!
  19. Infortrend存储 EonStor GSi 深度学习AI存储一体机
  20. 一篇文章看清楚 Linux 的职业发展方向

热门文章

  1. c++ 调用 python脚本, runtime error r6034
  2. petalinux 2018.2 在ubuntu 16下的安装
  3. 三阶齐次线性方程求通解_阶常系数齐次线性微分方程的通解证明
  4. FPGA: VGA显示
  5. pos机顾显java控制打印内容_Delphi下POS机控制钱箱,客显,打印机
  6. 远程控制---实验十:灰鸽子远控软件使用实验
  7. python threadpoolexecutor_python 线程池 ThreadPoolExecutor 的用法
  8. 长宁区对发明专利授权后给予每件不超过3000元的资助
  9. 里氏替换原则——面向对象设计原则
  10. 遥操作机器人解决方案