IOS内购验证 (Java版)

package xxxxx;

/**
 * @description 苹果验证返回结果状态码枚举
 * @Author xc
 * @Date 2021/3/12 18:23
 **/
public enum IosStatusCodeEnum {

    CODE_SUCCESS("0", "收据是有效, 验证成功"),
    CODE_NULL("-1", "苹果服务器没有返回验证结果"),
    CODE_21000("21000", "没有使用HTTP POST请求方法向App Store发出请求"),
    CODE_21001("21001", "这个状态码不再由App Store发送"),
    CODE_21002("21002", "receipt-data属性中的数据格式错误或服务遇到临时问题"),
    CODE_21003("21003", "这张收据无法证实真伪"),
    CODE_21004("21004", "您提供的共享秘密与您的帐户文件中的共享秘密不匹配"),
    CODE_21005("21005", "收据服务器暂时无法提供收据"),
    CODE_21006("21006", "订单是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中"),
    CODE_21007("21007", "这个收据来自测试环境,但是它被发送到生产环境进行验证"),
    CODE_21008("21008", "这个收据来自生产环境,但是它被发送到测试环境进行验证"),
    CODE_21009("21009", "内部数据访问错误"),
    CODE_21010("21010", "用户帐户找不到或已被删除");

    public String code;
    public String desc;
    IosStatusCodeEnum(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public static IosStatusCodeEnum getByCode(String code) {
        for (IosStatusCodeEnum value : values()) {
            if(value.code.equals(code)) return value;
        }
        return null;
    }

}
package xxxxx;

import lombok.Data;

/**
 * @description 所有应用内购买交易的应用内购买收据
 * @Author xc
 * @Date 2021/3/12 18:03
 **/
@Data
public class IosVerifyDTO {

    private String transaction_id; // 交易的唯一标识符
    private String original_purchase_date;
    private String quantity; // 购买的消费品数量
    private String original_transaction_id;
    private String purchase_date_pst; // 应用内购买的时间
    private String original_purchase_date_ms;
    private String purchase_date_ms;
    private String product_id; // IOS内购商品Id
    private String original_purchase_date_pst;
    private String is_trial_period;
    private String purchase_date;

}
package xxxxx;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import xxxxx.IosStatusCodeEnum;
import xxxxx.ServiceException;
import xxxxx.EnvironmentUtil;
import xxxxx.JsonUtils;
import xxxxx.IosVerifyDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.net.ssl.*;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.cert.X509Certificate;
import java.util.Locale;
import java.util.Optional;

/**
 * 
/**
 * @description IOS内购验证业务层
 * @Author xc
 * @Date 2021/3/12 18:45
 **/
@Service
public class IosVerifyService {

    // IOS内购 - 沙箱环境
    private static final String URL_SANDBOX = "https://sandbox.itunes.apple.com/verifyReceipt";
    // IOS内购 - 线上环境
    private static final String URL_VERIFY = "https://buy.itunes.apple.com/verifyReceipt";

    private static class TrustAnyTrustManager implements X509TrustManager {

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[] {};
        }
    }

    private static class TrustAnyHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }

    /**
     * 苹果支付验证(接口返回null则未查询到收据信息)
     *
     * @param receipt // 收据
     * @param transactionId // 交易的唯一标识符
     * @return
     */
    public IosVerifyDTO applePayVerify(String receipt, String transactionId) {
        // 当前交易标识符收据信息
        IosVerifyDTO iosVerifyDTO = null;
        String url;
        /**
         * 注 :
         *  EnvironmentUtil 为环境工具类,可根据各位实际情况而定,如没有多环境的项目,
         *     可以看下文章最末尾代码片段,无环境写法
         */
        if(EnvironmentUtil.isProd()){
            url = URL_VERIFY;
        }else {
            url = URL_SANDBOX;
        }
        String verifyResult = buyAppVerify(receipt, url);
        JSONObject resultJob = JSONObject.parseObject(verifyResult);
        String status = resultJob.getString("status"); // 苹果验证返回结果状态码
        /**
         * 注 :
         *  此处写法原因 :项目上线至 App Store 审核员支付审核使用的是沙箱支付;
         *  如没有此处代码,支付失败审核拒绝则被打回,需重新提交审核
         */
        if (IosStatusCodeEnum.CODE_21007.code.equals(status)) {
            verifyResult = buyAppVerify(receipt, URL_SANDBOX);
            resultJob = JSONObject.parseObject(verifyResult);
            status = resultJob.getString("status");
        }
        // 收据有效, 验证成功
        if (!IosStatusCodeEnum.CODE_SUCCESS.code.equals(status)) {
            log.error("【苹果服务器验证失败】返回验证结果。 transactionId: {}, status: {}, msg: {}", transactionId, status, IosStatusCodeEnum.getByCode(status).desc);
            return iosVerifyDTO;
        }
        String resultReceipt = resultJob.getString("receipt");
        JSONObject receiptJson = JSONObject.parseObject(resultReceipt);
        String in_app = receiptJson.getString("in_app"); // 苹果收据列表
        if(StringUtils.isEmpty(in_app)) {
            log.error("【苹果服务器验证失败】未查询到收据信息。 transactionId: {}, status: {}, msg: {}", transactionId, status, IosStatusCodeEnum.getByCode(status).desc);
            return iosVerifyDTO;
        }
        JSONArray in_app_array = JSONArray.parseArray(in_app);
        Optional<Object> optional = in_app_array.stream().filter(r ->
                (JsonUtils.fromJson(r.toString(), IosVerifyDTO.class).getTransaction_id().equals(transactionId))).findFirst();
        if(optional.isPresent()) iosVerifyDTO = JsonUtils.fromJson(optional.get().toString(), IosVerifyDTO.class);
        return iosVerifyDTO;
    }

    /**
     * 苹果服务器验证
     *
     * @param receipt 账单
     * @param url IOS内购核验环境地址
     * @url 要验证的地址
     * @return null 或返回结果
     */
    private String buyAppVerify(String receipt, String url) {
        try {
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
            URL console = new URL(url);
            HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
            conn.setSSLSocketFactory(sc.getSocketFactory());
            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type","application/json");
            conn.setRequestProperty("Proxy-Connection", "Keep-Alive");
            conn.setDoInput(true);
            conn.setDoOutput(true);
            BufferedOutputStream hurlBufOus = new BufferedOutputStream(conn.getOutputStream());
            // 拼成固定的格式传给平台
            String str = String.format(Locale.CHINA, "{\"receipt-data\":\"" + receipt + "\"}");
            hurlBufOus.write(str.getBytes());
            hurlBufOus.flush();

            InputStream is = conn.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            String line;
            StringBuffer sb = new StringBuffer();
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            return sb.toString();
        } catch (Exception e) {
            log.error("苹果服务器异常 errMsg: {}", e);
            /**
             * 注 :
             *  此处为自定义业务异常类
             */
            throw new ServiceException("苹果服务器异常");
        }
    }

}
/**
 * 注 :
 *    此处代码为无多环境写法
 *    可以将 IosVerifyService 类中 72-95行代码进行替换
 */
String verifyResult = buyAppVerify(receipt, URL_VERIFY);
JSONObject resultJob = JSONObject.parseObject(verifyResult);
String status = resultJob.getString("status"); // 苹果验证返回结果状态码
if (IosStatusCodeEnum.CODE_21007.code.equals(status)) {
    verifyResult = buyAppVerify(receipt, URL_SANDBOX);
    resultJob = JSONObject.parseObject(verifyResult);
    status = resultJob.getString("status");
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
以下是 PHP 代码实现 iOS 内购订单验证的示例: ```php <?php // 定义 app id 和 app secret $app_id = 'your_app_id'; $app_secret = 'your_app_secret'; // 获取验证请求参数 $receipt = $_POST['receipt']; $password = $_POST['password']; // 发送验证请求到 Apple 服务器 $url = 'https://buy.itunes.apple.com/verifyReceipt'; // 正式环境 //$url = 'https://sandbox.itunes.apple.com/verifyReceipt'; // 沙盒环境 $data = json_encode(array('receipt-data' => $receipt, 'password' => $password)); $options = array( 'http' => array( 'header' => "Content-type: application/json\r\n", 'method' => 'POST', 'content' => $data ) ); $context = stream_context_create($options); $result = file_get_contents($url, false, $context); // 解析验证结果 $response = json_decode($result, true); if ($response['status'] == 0) { // 验证成功 $latest_receipt_info = $response['latest_receipt_info']; $transaction_id = $latest_receipt_info[0]['transaction_id']; $product_id = $latest_receipt_info[0]['product_id']; $purchase_date = $latest_receipt_info[0]['purchase_date']; // TODO: 在这里处理订单验证成功后的逻辑 } else { // 验证失败 $error_code = $response['status']; $error_message = $response['status_message']; // TODO: 在这里处理订单验证失败后的逻辑 } ?> ``` 以上代码中,首先定义了 app id 和 app secret,然后获取了验证请求参数 receipt 和 password,接着发送验证请求到 Apple 服务器,并解析验证结果。如果验证成功,可以获取到订单的相关信息,如 transaction_id、product_id 和 purchase_date 等,可以在这里处理订单验证成功后的逻辑;如果验证失败,可以获取到错误码和错误信息,可以在这里处理订单验证失败后的逻辑。需要注意的是,验证请求需要发送到正式环境或者沙盒环境,根据实际情况选择相应的 URL 即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值