IOS支付,java后台代码

最近公司开发APP需要做IOS的支付功能,所以研究了一下IOS的支付,IOS支付和国内的微信支付宝支付流程有点不一样,IOS支付成功后也是依赖IOS APP回调的后台服务器,回调时携带IOS支付成功后的支付凭证等信息给服务器,不像微信支付宝是使用的他们的服务器通过REST API的方式回调,这可能和苹果是全球跨国企业,但有的国家对隐私做的比较严,不想暴露服务器的地址,所以才采用的这种方式,下面直接上代码。

1,开发前需要ios开发人员添加内购项目,如下:
在这里插入图片描述
IOS支付金额是固定的,不像微信支付宝那样可以随意输入支付金额,并且需要先在ios开发者后台添加配置好内购项目,参考名称和产品ID,可以设置成一样,那个也就是配置好的支付金额。

2,将配置好的内购项目转换成JSON格式,通过接口返回给IOS开发人员,我的格式定义如下:

[
  {
    "amount": 208,
    "productId": "208",
    "productName": "208"
  },
  {
    "amount": 518,
    "productId": "518",
    "productName": "518"
  },
  {
    "amount": 1098,
    "productId": "1098",
    "productName": "1098"
  },
  {
    "amount": 2298,
    "productId": "2298",
    "productName": "2298"
  },
  {
    "amount": 3298,
    "productId": "3298",
    "productName": "3298"
  },
  {
    "amount": 4498,
    "productId": "4498",
    "productName": "4498"
  }
]

3,IOS支付成功后通过APP回调服务器,完成整个的支付操作:
里面的服务类和具体的业务流程需要自己写,这里只贴出了ios验证的流程

@ApiOperation("ios支付成功后验证结果")
@RequestMapping(value = "/iPayNotify", method = RequestMethod.POST)
@ResponseBody
public ResultPageVo iPayNotify(@RequestBody IPayNotifyPo iPayNotifyPo) {

	log.debug("前段传递的ios支付参数=" + iPayNotifyPo.toString());

	String receipt = iPayNotifyPo.getTransactionReceipt();

	// 拿到收据的MD5
	String receiptMd5 = SecureUtil.md5(receipt);
	// 查询数据库,看是否是己经验证过的该支付收据
	boolean existsIOSReceipt = userOrderService.isExistsIOSReceipt(receiptMd5);
	if (existsIOSReceipt) {
		return ResultPageVo.failure("该充值已完成");
	}

	// 1.先线上测试    发送平台验证
	String verifyResult = IosVerifyUtil.buyAppVerify(receipt, 1);
	log.debug("1,苹果返回的参数=" + verifyResult);
	if (verifyResult == null) {
		// 苹果服务器没有返回验证结果
		log.debug("苹果服务器没有返回验证结果");
		return ResultPageVo.failure("订单没有找到");
	} else {
		// 苹果验证有返回结果
		JSONObject job = JSONUtil.parseObj(verifyResult);
		log.debug("2,苹果验证返回的json串=" + job.toString());
		String states = job.getStr("status");

		if ("21007".equals(states)) {

			log.debug("是沙盒环境,应沙盒测试,否则执行下面");

			// 是沙盒环境,应沙盒测试,否则执行下面
			// 2.再沙盒测试  发送平台验证
			verifyResult = IosVerifyUtil.buyAppVerify(receipt, 0);
			job = JSONUtil.parseObj(verifyResult);
			log.debug("3,沙盒环境验证返回的json字符串=" + job.toString());
			states = job.getStr("status");
		}
		if ("0".equals(states)) { // 前端所提供的收据是有效的    验证成功

			log.debug("前端所提供的收据是有效的    验证成功");

			String r_receipt = job.getStr("receipt");
			JSONObject returnJson = JSONUtil.parseObj(r_receipt);
			String in_app = returnJson.getStr("in_app");

			/**
			 * in_app说明:
			 * 验证票据返回的receipt里面的in_app字段,这个字段包含了所有你未完成交易的票据信息。也就是在上面说到的APP完成交易之后,这个票据信息,就会从in_app中消失。
			 * 如果APP不完成交易,这个票据信息就会在in_app中一直保留。(这个情况可能仅限于你的商品类型为消耗型)
			 *
			 * 知道了事件的原委,就很好优化解决了,方案有2个
			 * 1.对票据返回的in_app数据全部进行处理,没有充值的全部进行充值
			 * 2.仅对最新的充值信息进行处理(我们采取的方案)
			 *
			 * 因为采用一方案:
			 * 如果用户仅进行了一次充值,该充值未到账,他不再进行充值了,那么会无法导致。
			 * 如果他通过客服的途径已经进行了补充充值,那么他在下一次充值的时候依旧会把之前的产品票据带回,这时候有可能出现重复充值的情况
			 *
			 * 以上说明是我在网上找到的,可以查看原文
             * https://www.cnblogs.com/widgetbox/p/8241333.html
			 */

			JSONArray jsonArray = JSONUtil.parseArray(in_app);
			if (jsonArray.size() > 0) {
				int index = 0;
				JSONObject o = JSONUtil.parseObj(jsonArray.get(index));
                String transaction_id = o.getStr("transaction_id"); // 订单号
                String product_id = o.getStr("product_id"); // 产品id,也就是支付金额
                String purchase_date_ms = o.getStr("purchase_date_ms"); // 支付时间

				// 添加支付金额
				userOrderService.iosChargeSuccess(transaction_id, product_id, purchase_date_ms, receiptMd5);
			}
		} else {
			return ResultPageVo.failure("收到数据有误");
		}
	}
	return ResultPageVo.success();
}

Po参数封装类:

@Data
public class IPayNotifyPo {

    @ApiModelProperty("苹果支付凭证")
    private String transactionReceipt;

    @ApiModelProperty("苹果支付单号")
    private String payId;

    @ApiModelProperty("用户id")
    private Integer userId;

}

IosVerifyUtil 工具类:

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.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Locale;

/**
 * @desc: 苹果IAP内购验证工具类
 * @author: hwm
 * @date: 2019/9/3 17:11
 */
public class IosVerifyUtil {

    private static class TrustAnyTrustManager implements X509TrustManager {

        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

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

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

    // 沙盒环境
    private static final String url_sandbox = "https://sandbox.itunes.apple.com/verifyReceipt";
    // 生产环境
    private static final String url_verify = "https://buy.itunes.apple.com/verifyReceipt";

    /**
     * 苹果服务器验证
     *
     * @param receipt 账单
     * @return null 或返回结果 沙盒 https://sandbox.itunes.apple.com/verifyReceipt
     * @url 要验证的地址
     */
    public static String buyAppVerify(String receipt, int type) {
        //环境判断 线上/开发环境用不同的请求链接
        String url = "";
        if (type == 0) {
            url = url_sandbox; //沙盒测试
        } else {
            url = url_verify; //线上测试
        }
        //String url = EnvUtils.isOnline() ?url_verify : url_sandbox;

        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", "text/json");
            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 = null;
            StringBuffer sb = new StringBuffer();
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }

            return sb.toString();
        } catch (Exception ex) {
            System.out.println("苹果服务器异常");
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * 用BASE64加密
     *
     * @param str
     * @return
     */
    public static String getBASE64(String str) {
        byte[] b = str.getBytes();
        String s = null;
        if (b != null) {
            s = new sun.misc.BASE64Encoder().encode(b);
        }
        return s;
    }
}

使用到的JSON工具类pom导入,也可以改成自己项目里面的工具类稍微改下就好了

<!-- 工具包:https://hutool.cn/docs/ -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>4.6.1</version>
</dependency>

最后贴一段沙盒环境凭证验证成功的json数据结果:

{
  "environment": "Sandbox",
  "receipt": {
    "in_app": [
      {
        "transaction_id": "1000000564723363",
        "original_purchase_date": "2019-09-04 11:51:19 Etc/GMT",
        "quantity": "1",
        "original_transaction_id": "1000000564723363",
        "purchase_date_pst": "2019-09-04 04:51:19 America/Los_Angeles",
        "original_purchase_date_ms": "1567597879000",
        "purchase_date_ms": "1567597879000",
        "product_id": "208",
        "original_purchase_date_pst": "2019-09-04 04:51:19 America/Los_Angeles",
        "is_trial_period": "false",
        "purchase_date": "2019-09-04 11:51:19 Etc/GMT"
      }
    ],
    "adam_id": 0,
    "receipt_creation_date": "2019-09-04 11:51:19 Etc/GMT",
    "original_application_version": "1.0",
    "app_item_id": 0,
    "original_purchase_date_ms": "1375340400000",
    "request_date_ms": "1567597892412",
    "original_purchase_date_pst": "2013-08-01 00:00:00 America/Los_Angeles",
    "original_purchase_date": "2013-08-01 07:00:00 Etc/GMT",
    "receipt_creation_date_pst": "2019-09-04 04:51:19 America/Los_Angeles",
    "receipt_type": "ProductionSandbox",
    "bundle_id": "com.Lanhu.project.SixPigeonClass",
    "receipt_creation_date_ms": "1567597879000",
    "request_date": "2019-09-04 11:51:32 Etc/GMT",
    "version_external_identifier": 0,
    "request_date_pst": "2019-09-04 04:51:32 America/Los_Angeles",
    "download_id": 0,
    "application_version": "1"
  },
  "status": 0
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值