苹果支付服务器校验

苹果支付回调核心是校验凭证的有效性,同时防止利用低价格商品凭证校验高价格商品

//一个凭证下可能会存在多个订单凭证,所以最好进行自动补单。利用第三方订单幂等原则,生成唯一订单id

我们实现机制是客户端下单支付,完成之后 客户端向服务器发起回调,服务器进行相应的订单回调(同时检测,实现补单功能)

//回调函数

private Map<String, Object> doCallBack(ApplePurchaseBO purchaseBO) {
    // 检查服务器是否已经保存 并获取支付的url类型
    ApplePayUrlTypeEnum payUrlTypeEnum = checkAndSaveCertificateFmDB(purchaseBO);
    // 去苹果官网验证凭证的真实性
    AppleCertificateBO certificateBO = getAndCheckCertificateFmApple(purchaseBO.getCertificate(), payUrlTypeEnum);
    ShopOrder shopOrder = shopService.getOrder(purchaseBO.getOid());
    // 校验是否为我们平台的凭证
    checkBundleId(shopOrder, certificateBO.getBundleId());
    // 校验订单回掉的有效性,如果是新版本且尚未回掉,则进行补单 操作
    checkTIdPIdAndSupplementOrder(shopOrder, purchaseBO, certificateBO);
    return getResult(purchaseBO);

}

private ApplePurchaseBO getRequestDecorate(Object originRequest) {
    boolean isHttpRequest = false;
    HttpServletRequest request = null;
    ApplePayNotifyDTO notifyDTO = null;
    Map<String, String> params = null;
    if (originRequest instanceof HttpServletRequest) {
        isHttpRequest = true;
        request = (HttpServletRequest) originRequest;
        params = UrlUtils.convertRequestBodyToMap(request);
        logger.info("[ApplePay][callBack] http body <{}>", params);

    } else {
        notifyDTO = (ApplePayNotifyDTO) originRequest;
        logger.info("[ApplePay][callBack] interface body <{}>", notifyDTO);
    }

    return new ApplePurchaseBO()
            .setOid(isHttpRequest ? params.get("oid") : notifyDTO.getOid())
            .setCertificate(isHttpRequest ? params.get("receipt") : notifyDTO.getCertificate())
            .setOriginTransactionId(isHttpRequest ? params.get("transactionId") : notifyDTO.getTransactionId())
            .setOriginProductId(isHttpRequest ? params.get("productId") : notifyDTO.getProductId())
            .setChannel(isHttpRequest ? request.getHeader("app_channel") : notifyDTO.getChannel())
            .setVersion(isHttpRequest ? request.getHeader("app_version") : notifyDTO.getVersion())
            .setPackageName(isHttpRequest ? request.getHeader("app_package_name") : notifyDTO.getPackageName())
            .setSubChannel(isHttpRequest ? request.getHeader("sub_channel") : "")
            .setHttpRequest(isHttpRequest)
            .setPayType(PayTypeEnum.Apple.payType())
            .setDeviceType(DeviceTypeEnum.ios.type())
            .setUid(isHttpRequest ? Long.valueOf(request.getHeader("uid")) : notifyDTO.getUid())
            .setNewVersion(isHttpRequest && newVersion.equals(params.get("pay_version")));
}

private ApplePayUrlTypeEnum checkAndSaveCertificateFmDB(ApplePurchaseBO purchaseBO) {

    boolean paramError = StringUtils.isEmpty(purchaseBO.getCertificate())
            || StringUtils.isEmpty(purchaseBO.getOid())
            || StringUtils.isEmpty(purchaseBO.getOriginProductId())
            || purchaseBO.getUid() == null
            || StringUtils.isEmpty(purchaseBO.getOriginTransactionId());
    if (paramError) {
        throw new BusinessException(ResultEnum.PARA_ERR);
    }

    ApplePayUrlTypeEnum payUrlTypeEnum;
    ApplePurchaseCertificate certificateInfo = certificateService.getCertificateByOidTransactionId(purchaseBO.getOid(), purchaseBO.getOriginTransactionId());
    if (certificateInfo == null) {
        payUrlTypeEnum = getPayUrl(purchaseBO.getChannel(), purchaseBO.getVersion(), purchaseBO.getPackageName());
        saveApplePurchaseCertificate(purchaseBO.getOid(), purchaseBO.getCertificate(), purchaseBO.getOriginProductId(), purchaseBO.getOriginTransactionId(), payUrlTypeEnum);
    } else {
        if (AppleCertificateStateEnum.fake.state().equals(certificateInfo.getState())) {
            throw new BusinessException(ResultEnum.APPLE_PAY_GOODS_EXIST);
        }
        payUrlTypeEnum = ApplePayUrlTypeEnum.find(certificateInfo.getPayUrlType());
    }
    return payUrlTypeEnum;
}

//凭证校验
private AppleCertificateBO getAndCheckCertificateFmApple(String certificate, ApplePayUrlTypeEnum payUrlTypeEnum) {
    Object o = JSON.parseObject(getAppleRspBody(certificate, payUrlTypeEnum.url()));
    Object receipt = ((JSONObject) o).get("receipt");

    if ("0".compareTo(((JSONObject) o).get("status").toString()) != 0) {
        dealException(o);
    }
    AppleCertificateBO certificateBO = JSONObject.parseObject(receipt.toString(), AppleCertificateBO.class);
    boolean isFakeCertificate = certificateBO == null || certificateBO.getInAppArray() == null || certificateBO.getInAppArray().length == 0;
    if (isFakeCertificate) { //校验凭证规则
        throw new BusinessException(ResultEnum.APPLE_PAY_RECEIPT_UNUSUAL);
    }
    return certificateBO;
}

private void checkBundleId(ShopOrder shopOrder, String bundleId) {
    boolean isApplePay = shopOrder != null && shopOrder.getPayType().equals(PayTypeEnum.Apple.payType());
    if (appPackageManager.getAppPackage(bundleId) == null || !isApplePay) {
        throw new BusinessException(ResultEnum.APPLE_PAY_RECEIPT_UNKNOW);
    }
}

private void checkTIdPIdAndSupplementOrder(ShopOrder shopOrder, ApplePurchaseBO purchaseBO, AppleCertificateBO certificateBO) {
    List<InApplePurchaseBO> inApplePurchaseBOS = new ArrayList<>();
    boolean isFakeGood = true;
    String transactionId;
    String productionId;

    if (!purchaseBO.getOriginProductId().equals(goodsManager.getGoodsByGoodsId(shopOrder.getGid()).getProductId())) {
        throw new BusinessException(ResultEnum.APPLE_PAY_GOODS_EXIST);
    }

    for (int i = 0; i < certificateBO.getInAppArray().length; i++) {
        productionId = certificateBO.getInAppArray()[i].getProductId();
        transactionId = certificateBO.getInAppArray()[i].getTransactionId();
        boolean isFind = transactionId.equals(purchaseBO.getOriginTransactionId()) && purchaseBO.getOriginProductId().equals(productionId);
        if (isFind) {
            goodsManager.getGoodsByProductId(purchaseBO.getOriginProductId());
            isFakeGood = false;
        } else {//如果未找到凭证 且是 新版本的则进行补单操作
            boolean supplementOrder = purchaseBO.getNewVersion() && certificateService.getCertificateByTId(transactionId) == null;
            if (supplementOrder) {
                inApplePurchaseBOS.add(new InApplePurchaseBO(transactionId, productionId));
            }
        }
    }
    supplementOrder(purchaseBO, inApplePurchaseBOS);
    if (isFakeGood) {  //判断商品是不是有效
        certificateService.updateApplePurchaseCertificateByOid(purchaseBO.getOid(), AppleCertificateStateEnum.fake, AppleCertificateStateEnum.init);
        throw new BusinessException(ResultEnum.APPLE_PAY_GOODS_EXIST);
    }
}


private void saveApplePurchaseCertificate(String oid, String certificate, String productionId, String transactionId, ApplePayUrlTypeEnum urlTypeEnum) {

    ApplePurchaseCertificate purchaseCertificate = new ApplePurchaseCertificate();
    purchaseCertificate.setOid(oid);
    purchaseCertificate.setCertificate(certificate);
    purchaseCertificate.setProductId(productionId);
    purchaseCertificate.setTransactionId(transactionId);
    purchaseCertificate.setPayUrlType(urlTypeEnum.urlType());
    certificateService.savePurchaseCertificate(purchaseCertificate);


}


@Override
public Map<String, Object> queryPay(String oid, String certificate) {
    return null;
}


private String getAppleRspBody(String purchaseCertificate, String url) {
    String responseBody = HttpInvokeUtil.httpsPostJson(url, JSON.parseObject(JSON.toJSONString(ImmutableMap.of("receipt-data", purchaseCertificate))));
    logger.info("[ApplePay][callBack] responseBody:{}", JSON.toJSONString(responseBody));
    return responseBody;
}

private Map<String, Object> getResult(ApplePurchaseBO purchaseBO) {
    Map<String, Object> result = new HashMap<>();
    result.put(PayConstant.certificate, purchaseBO.getCertificate());
    result.put(PayConstant.oid, purchaseBO.getOid());
    result.put(PayConstant.result, PayConstant.paySuccess);
    return result;

}

private void dealException(Object o) {

    switch (((JSONObject) o).get("status").toString()) {
        case "21002":
            throw new BusinessException(ResultEnum.APPLE_PAY_RECEIPT_ERROR02);
        case "21003":
            throw new BusinessException(ResultEnum.APPLE_PAY_RECEIPT_ERROR03);
        case "21005":
            throw new BusinessException(ResultEnum.APPLE_PAY_RECEIPT_ERROR05);
        case "21007":
            throw new BusinessException(ResultEnum.APPLE_PAY_RECEIPT_ERROR07);
        case "21008":
            throw new BusinessException(ResultEnum.APPLE_PAY_RECEIPT_ERROR08);
        default:
            break;
    }
    throw new BusinessException(ResultEnum.PAY_THIRD_FAIL);

}

private ApplePayUrlTypeEnum getPayUrl(String channel, String version, String packageName) {
    ApplePaymentConfigVO applePaymentConfigVO = null;
    if (channel != null && version != null && packageName != null) {
        applePaymentConfigVO = payConfigService.getApplePaymentConfigVO(channel, version, packageName);
    }
    if (applePaymentConfigVO != null) {
        return ApplePayUrlTypeEnum.find(applePaymentConfigVO.getType());
    }
    return ApplePayUrlTypeEnum.production;
}


private boolean canRetry(Exception e) {
    if (e instanceof BusinessException) {
        ResultEnum resultEnum = ((BusinessException) e).getResultEnum();
        if (resultEnum != null) {
            return false;
        }
    }
    return true;
}

private void playDoRetryNotify(ApplePayNotifyDTO notifyDTO) {
    applePayNotifyExecutor.execute(new CasFailOverRunnable(3) {
        @Override
        public int doRun() {
            Map<String, Object> res = payService.onPayResultNotify(PayTypeEnum.Apple.payType(), notifyDTO);
            if (PayConstant.paySuccess.equals(res.get(PayConstant.result))) {
                return 1;
            }
            return 0;
        }
    });
}

private void supplementOrder(ApplePurchaseBO purchaseBO, List<InApplePurchaseBO> inApplePurchaseBOS) {
    if (inApplePurchaseBOS.isEmpty()) {
        return;
    }
    supplementOrderExecutor.execute(new CasFailOverRunnable(3) {
        @Override
        public int doRun() {
            for (InApplePurchaseBO inApplePurchaseBO : inApplePurchaseBOS) {
                logger.info("[ApplePay][callBack] ApplePurchaseBO :{} inApplePurchaseBO  <{}>", purchaseBO, inApplePurchaseBO);
                Goods goods = goodsManager.getGoodsByProductId(inApplePurchaseBO.getProductId());
                ShopOrderApplyResponse response = shopService.applyOrder(purchaseBO.getUid(), ShopOrderApplyRequest.covert(inApplePurchaseBO.getProductId(), inApplePurchaseBO.getTransactionId(), goods, purchaseBO));
                playDoRetryNotify(ApplePayNotifyDTO.covert(purchaseBO, response.getOid(), inApplePurchaseBO.getTransactionId(), inApplePurchaseBO.getProductId()));
            }
            return 1;
        }
    });


}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Flutter是一种跨平台的移动应用开发框架,它可以让开发者只写一次代码,就能同时在iOS和Android两个平台上运行应用程序。而苹果支付苹果公司针对iOS平台提供的一种在线支付服务。 在Flutter中,苹果支付提供了一个插件(flutter_inapp_purchase)来支持在iOS设备上实现购买功能。开发者可以使用这个插件来实现用户在应用内进行购买的功能,比如购买虚拟商品、订阅服务等。 使用苹果支付的过程大致如下: 1. 在应用程序中引入flutter_inapp_purchase插件,并在应用程序中注册相关的购买项目和付款回调。 2. 在用户需要购买商品的地方,调用相关的接口发起购买请求。 3. 用户在弹出的购买界面中确认购买并完成支付。 4. 在支付完成后,系统会返回一个支付结果,开发者可以根据这个结果来判断购买是否成功,并进行相应的处理。 值得注意的是,苹果支付需要开发者在Apple Developer网站上创建并配置应用程序的相关信息,比如App ID、证书等。并且使用苹果支付进行购买时,苹果会对购买进行审核,审核通过后才能正式上线。 总的来说,Flutter可以非常方便地实现在iOS设备上使用苹果支付进行购买的功能,开发者只需要引入对应的插件并进行简单的配置和调用即可。这样用户就能够在Flutter应用中方便地完成购买操作,提高用户体验,同时也为开发者带来更好的商业机会。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值