精华内容
下载资源
问答
  • 本博客是经历过多个项目检验的, 绝对真实, 适应于对苹果iap内购稍微有些了解的JAVA开发人员, 认真看, 定能完美解决苹果内购问题. 苹果IAP内购支付实际上是"将客户端支付后的一些信息传给后台, 后台拿着这些信息在...

    1.前言

          本博客是经历过多个项目检验的, 绝对真实, 适应于对苹果iap内购稍微有些了解的JAVA开发人员,  认真看,  定能完美解决苹果内购问题.

          苹果IAP内购支付实际上是"将客户端支付后的一些信息传给后台,  后台拿着这些信息在传给苹果支付平台,  来验证客户端支付是否有效"的一个过程, 中间的难点有三个.

          一是沙盒测试数据和线上测试数据的问题. 刚开始接入苹果内购时,网上的各种测试一大堆, 几乎你就找不到两篇相同的数据, 这导致我刚开始做的时候踩了很多坑,  对于这种情况建议各位java开发者以实际测试数据为准,  因为我认为, 随着时间往后, 苹果平台返回的测试数据还会变化, 请以真实得到的数据为准.  下面贴一下我当时项目的测试数据(这个项目是2017年11月左右的数据)

    这个是沙箱测试数据(经过检验,正式的测试数据与沙箱测试数据结构是一样,所以采用同一套代码即可)

    {
    "status": 0,
    "environment": "Sandbox",
    "receipt": {
    "receipt_type": "ProductionSandbox",
    "adam_id": 0,
    "app_item_id": 0,
    "bundle_id": "com.jiuying.twelveAnimal",
    "application_version": "0",
    "download_id": 0,
    "version_external_identifier": 0,
    "receipt_creation_date": "2018-01-05 10:06:12 Etc/GMT",
    "receipt_creation_date_ms": "1515146772000",
    "receipt_creation_date_pst": "2018-01-05 02:06:12 America/Los_Angeles",
    "request_date": "2018-01-05 10:06:14 Etc/GMT",
    "request_date_ms": "1515146774645",
    "request_date_pst": "2018-01-05 02:06:14 America/Los_Angeles",
    "original_purchase_date": "2013-08-01 07:00:00 Etc/GMT",
    "original_purchase_date_ms": "1375340400000",
    "original_purchase_date_pst": "2013-08-01 00:00:00 America/Los_Angeles",
    "original_application_version": "1.0",
    "in_app": [
    {
    "quantity": "1",
    "product_id": "com.jiuying.twelveAnimal.6", //这个6是关键, 6就是苹果客户端支付的金额, 我们取到这个值,进行我们的业务逻辑即可
    "transaction_id": "1000000364151484",
    "original_transaction_id": "1000000364151484",
    "purchase_date": "2018-01-05 10:06:11 Etc/GMT",
    "purchase_date_ms": "1515146771000",
    "purchase_date_pst": "2018-01-05 02:06:11 America/Los_Angeles",
    "original_purchase_date": "2018-01-05 10:06:11 Etc/GMT",
    "original_purchase_date_ms": "1515146771000",
    "original_purchase_date_pst": "2018-01-05 02:06:11 America/Los_Angeles",
    "is_trial_period": "false"
    }
    ]
    }
    }
    得到前端支付数据,发给苹果平台,进行二次验证
           

    /**
    * @throws Exception
    * 苹果内购支付
    * @Title: doIosRequest
    * @Description:Ios客户端内购支付
    * @param TransactionID :交易单号 需要客户端传过来的参数1
    * @param Payload:需要客户端传过来的参数2
    * @throws
    */
    public Map<String, Object> doIosRequest(String TransactionID,String Payload, int userId) throws Exception {
    Map<String, Object> map = new HashMap<String, Object>();
    Map<String, Object> mapChange = new HashMap<String, Object>();
    System.out.println("客户端传过来的值1:"+TransactionID+"客户端传过来的值2:"+Payload);

    String verifyResult = IosVerifyUtil.buyAppVerify(Payload,1); //1.先线上测试 发送平台验证
    if (verifyResult == null) { // 苹果服务器没有返回验证结果
    System.out.println("无订单信息!");
    } else { // 苹果验证有返回结果
    System.out.println("线上,苹果平台返回JSON:"+verifyResult);
    JSONObject job = JSONObject.parseObject(verifyResult);
    String states = job.getString("status");

    if("21007".equals(states)){ //是沙盒环境,应沙盒测试,否则执行下面
    verifyResult = IosVerifyUtil.buyAppVerify(Payload,0); //2.再沙盒测试 发送平台验证
    System.out.println("沙盒环境,苹果平台返回JSON:"+verifyResult);
    job = JSONObject.parseObject(verifyResult);
    states = job.getString("status");
    }

    System.out.println("苹果平台返回值:job"+job);
    if (states.equals("0")){ // 前端所提供的收据是有效的 验证成功
    String r_receipt = job.getString("receipt");
    JSONObject returnJson = JSONObject.parseObject(r_receipt);
    String in_app = returnJson.getString("in_app");
    JSONObject in_appJson = JSONObject.parseObject(in_app.substring(1, in_app.length()-1));

    String product_id = in_appJson.getString("product_id");
    String transaction_id = in_appJson.getString("transaction_id"); // 订单号
    /************************************************+自己的业务逻辑**********************************************************/
    //如果单号一致 则保存到数据库
    if(TransactionID.equals(transaction_id)){
    String [] moneys = product_id.split("\\.");
    //System.out.println("用户ID:"+userId+",要充值的钻石数:"+moneys[moneys.length-1]);
    mapChange = charge(Integer.parseInt(moneys[moneys.length-1]), 5, userId);
    map.put("money", moneys[moneys.length-1]);
    }
    /************************************************+自己的业务逻辑end**********************************************************/
    if((boolean) mapChange.get("success")){//用户钻石数量新增成功
    map.put("success", true);
    map.put("message", "充值钻石成功!");
    //map.put("status", states);
    }else{
    map.put("success", false);
    map.put("message", "充值钻石失败!");
    }
    } else {
    map.put("success", false);
    map.put("message", "receipt数据有问题");
    map.put("status", states);
    }
    }
    return map;
    }
    上面的方法用到了一个工具类,如下
      

    package com.miracle9.animal.util;

    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;

    import javax.net.ssl.HostnameVerifier;
    import javax.net.ssl.HttpsURLConnection;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSession;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;

    /**
    * 苹果IAP内购验证工具类
    * @ClassName: IosVerify
    * @Description:Apple Pay
    */
    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
    * 账单
    * @url 要验证的地址
    * @return null 或返回结果 沙盒 https://sandbox.itunes.apple.com/verifyReceipt
    *
    */
    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("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;
    }

    }
    ---------------------
    作者:艺术2333
    来源:CSDN
    原文:https://blog.csdn.net/jianzhonghao/article/details/79343887
    版权声明:本文为博主原创文章,转载请附上博文链接!

    转载于:https://www.cnblogs.com/zxtceq/p/10237643.html

    展开全文
  • iOS购买商品流程:iOS购买商品的流程比较复杂,app付费商品都是放到app store中,涉及到和苹果服务器的交互,所以我大概画了一个流程图,之后的测试步骤也是根据这个流程拆分的可以看到从我方app到苹果服务器的...
    0965117c5cdf29840685c5e673b64bed.png

    iOS购买商品流程:

    iOS购买商品的流程比较复杂,app内付费商品都是放到app store中,涉及到和苹果服务器的交互,所以我大概画了一个流程图,之后的测试步骤也是根据这个流程拆分的

    可以看到从我方app到苹果服务器的购买成功的流程,一共发送了8次请求+响应

    第1次:客户端上选中商品并点击购买按钮,将购买请求发送到苹果服务器

    第2次:

    1.根据购买请求,苹果服务器购买对应的商品,

    2.弹出对应商品信息弹窗让用户确认购买信息,并让用户输入账号和密码购买

    3.购买成功后将扣款结果和票据信息返回客户端,并弹出完成购买弹窗

    第3次:我方app客户端将票据信息发送到我方app服务端,

    第4次:服务端拿到票据信息,去苹果服务器校验票据信息是否正确

    第5次:返回校验结果,服务端根据票据信息再次购买后台对应商品,相关数据库表数据更新

    第6次:服务端将购买校验结果返回到客户端,客户端得到购买结果并弹出购买成功弹窗

    第7次:客户端得到购买结果后,发起请求到服务端,拉取最新的用户信息

    第8次:服务端将用户信息返回客户端,客户端刷新页面

    测试环境iOS商品购买

    沙盒环境是什么

    我们在购买ios虚拟商品时,不知道你有没有发现它无法使用第三方支付,这是因为购买操作是在App Store中进行的,所以我们在测试购买功能时,需要在App Store内我们的app下创建内购商品,app审核未通过还未上线时默认是苹果测试环境(也叫沙盒环境,沙箱环境,sandbox)

    6fbc25d14ccb1893c315f41423bfaf26.png

    测试环境的购买流程和正式是一样的,上线后创建的商品立即变成线上的

    选择好之后开始创建,需要输入标题,产品ID,价格等

    840f8a5010a075854a44febd06c8a579.png

    内购项目类型有四种

    第一种是消耗型项目,它购买后只能使用一次,使用后失效,比如代币,购买完代币后,代币可以继续购买app其他商品

    第二种是非消耗型项目,只需要购买一次,无需重复购买,这个是和appleID绑定的,比如游戏的道具,后面即使卸载游戏再重新安装,道具都存在

    第三种是自动续期订阅,这个是首次购买后,之后每次时限到期都会自动续费,无需再输密码,也不用重新购买,除非手动取消,比如连续包月会员

    第四种是非续期订阅,这个是有时限性的产品,可以重复购买,每次购买时效都会累加

    测试方法

    根据测试流程,对每一步进行校验;

    通过查看或构造接口,查看日志,查看数据库信息,等等手段,确定问题出现在哪一步

    5cc0180273d9f5eb1631235990ec4f9a.png

    这就是我对iOS的app内购买测试的一点经验和心得,欢迎留言讨论哦~

    展开全文
  • Unity接入苹果内购(IAP)项目中需要接入苹果内购,由于本人没有任何接入支付的经验,所以这两天各种百度、谷歌查资料,测试过后发现网上好多资料都过时了,经过几天折腾测试项目终于通过了苹果的沙盒测试环节,虽然...

    前言

    第一次发帖,有点激动嘿嘿!话不多说直接奔主题,项目中需要接入苹果内购,由于本人没有任何接入支付的经验,所以这两天各种百度、谷歌查资料,测试过后发现网上好多资料都过时了,经过几天折腾测试项目终于通过了苹果的沙盒测试环节,虽然最终成功了,但是脑子里没有一套完整的流程,故整理下这两天的工作并在此记录下自己的心得以备之后不时之需,有很多的不足之处欢迎大佬指教。

    苹果支付流程

    在接苹果支付前我们需要先来大体了解下苹果支付的流程是怎样的:
    苹果支付流程图
    大体可以理解为我们要想在自己的app里面购买自己定义的商品,首先我们先把自己的商品注册在apple服务器上然后去购买apple服务器上我们注册的商品并完成支付,为了安全性考虑(防止越狱机等),我们一般都会在支付后有一步支付凭证的验证过程来确保商品确实是经过正规途径购买的,具体流程慢慢往下看好了!

    配置App

    首先我们需要到App Store Connect 这里配置一下你的app应用,在这里插入图片描述
    当然要想进入这之前你需要登录开发者账号,如果进入的不是这个界面可能你的账号是未付费的或者权限不够,我是用公司的开发者账号登录的。然后点击 我的app 会进入到这个界面:

    在这里插入图片描述
    如果你以前没有创建过那么这地方会是空的,这里会显示你以前创建过的所有app应用,点击应用就可以看见以前的设置信息,我们先创建一个应用,点击左上方的+号创建 选择 新建App 会进入到如下界面:在这里插入图片描述
    其实这些设置都有官方的说明文档,可以去看看,填写完就可以创建我们的app应用了。创建完我们会进入到下面那个界面在这里插入图片描述

    因为我们只是测试一下沙盒环境下的支付,所以这些信息不用填写!

    配置商品

    接下来我们就要配置一下我们app内的商品信息了,找到下面这个界面:
    在这里插入图片描述
    然后点击+号来添加我们的商品,商品类型分为以下几类:在这里插入图片描述
    示例里面已经把这几类商品介绍的很清楚了,这就不一一赘述了,选择自己需要创建的商品类型,这里为了测试方便选择了第一种
    在这里插入图片描述

    在这里插入图片描述
    上面的信息除了推广外其余最好都填写,因为不填写会报此商品元数据缺失,我也不知道后续会有啥问题,屏幕快照随便找一个640*920的图片上传就可以,其他分辨率可能上传不成功!然后我们商品基本信息设置完成,点击右上角存储然后会发现这个商品已经添加到我们的商品列表中,并且商品状态变成准备提交状态。还有最后容易忽略的一步,非常重要,我就是因为这步没有设置导致找不到商品信息浪费了不少时间,进入App Store下的准备提交界面找到APP内购买项目 添加我们的配置的商品信息,如下:在这里插入图片描述
    这里选择刚刚我们创建的商品添加进去就可以了

    协议、税务和银行业务

    一开始忘记配置这的信息了,导致打出的ios包在真机上总是报商品初始化不成功错误,折腾了好久才发现公司账户没有配置银行信息,-_-||,还有一点这些设置最好在苹果上面配置(包括下面的沙盒账号申请),一开始我是在windows上面配置的,到了这一步发现会出现各种奇葩的界面,估计是浏览器或者系统的问题(当然也有可能是我的问题。。。),如果公司以前没有配置过的话进入后只有一个免费App一项,然后我们可以申请付费app协议,由于我已经配置过没有发现创建项,我就不在此一一赘述了,可以去这儿看看详细步骤,因为是以前的帖子一些界面有所改动,不过仔细看看问题不大!还有这个里面的银行账户设置后是可以修改的你可以配置公司的账户,当然也可以为了测试添加自己的账户也是可以的!配置完成后等待苹果审核通过,我当时等了大概不到一小时就显示审核通过了,通过后我们的商品配置基本完成!

    沙盒测试账号

    苹果支付分为真实环境和沙盒环境,真实环境就是你用自己的苹果账号购买商品(会真实扣费的),沙盒环境就是苹果专门为支付测试而生的一种测试账号(支付成功也不会扣费),说白了就是你的app只要没有通过苹果审核没有正式上线,所有的支付都是沙盒环境的,如果你登录自己的苹果账号去测试会出错的,沙盒环境下就需要我们去使用沙盒账号去测试支付,沙盒账号我们可以去下面这去申请:
    在这里插入图片描述
    额,这我是在mac上面申请的,因为window上面显示不出来这个界面,然后自己创建一个测试账号就可以了,没啥大问题就不截图了,按照提示来就可以!
    以上就是测试苹果支付的商品注册步骤,基本没啥大问题,接下来梳理一下unity里面IAP的使用

    Unity(IAP)

    IAP,是in-App Purchase的缩写,可以理解为在App内购买,这也是为何IAP又被称为内购的原因。
    苹果规定,凡是在App内提供的服务需要付费时,必须使用IAP,比如说游戏的金币,道具等;而在App外提供的服务需要付费时,可以使用其他的支付方式,比如支付宝SDK、微信SDK等。说的更通俗一点,如果付费购买的商品是虚拟商品,比如游戏中的道具,并不是现实中存在的,那么必须使用IAP;
    首先我们先在我们的项目中下载IAP,有两种方式你可以选择去assetstore去下载,也可以找到这个界面:在这里插入图片描述
    把标识的打开会进入到这个界面:在这里插入图片描述
    如果你已经下载过这里会变成Update如果没有点击导入就好了,等待完成,然后你项目里会多出一些东西,其实下面这些你可以看unity官方的IAP文档就可以,比较详细。客户端添加商品有两种方法,一种是window窗口可以创建IAP Button,这种方法可以自己百度,我觉得太麻烦就用的第二种,第二种方法就是自己建立一个管理类自己管理自己的商品,代码如下:

    
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Net;
    using System.Text;
    using LitJson;
    using UnityEngine;
    using UnityEngine.Purchasing;
    using UnityEngine.Networking;
    [Serializable]
    public class Products
    {
        public string id;
        public int productType;
    }
    /// <summary>
    /// 购买管理理
    /// </summary>
    public class PurchaseManager : MonoBehaviour, IStoreListener
    {
        public List<Products> products = new List<Products>();
        public string publicKey;
        ConfigurationBuilder builder;
        private IStoreController m_Controller;
        private IAppleExtensions m_AppleExtensions;
        private int productIndex;
        private static bool isInited = false;
        private bool isInitFailed = false;
        void Awake()
        {
            if (!isInited)
            {
                InitPurchase();
            }
        }
        /// <summary>
        /// 初始化
        /// </summary>
        void InitPurchase()
        {
            Debug.Log("初始化");
            var module = StandardPurchasingModule.Instance();
            builder = ConfigurationBuilder.Instance(module);
            for (int i = 0; i < products.Count; i++)
            {
                builder.AddProduct(products[i].id,
                (ProductType)products[i].productType);
            }
            UnityPurchasing.Initialize(this, builder);
        }
        /// <summary>
        /// 初始化成功
        /// </summary>
        /// <param name="controller">Controller.</param>
        /// <param name="extensions">Extensions.</param>
        public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
        {
            //Debug.Log("初始化成功");
            m_Controller = controller;
            m_AppleExtensions =extensions.GetExtension<IAppleExtensions>();
            m_AppleExtensions.RegisterPurchaseDeferredListener(OnDeferred);
            isInited = true;
        }
        /// <summary>
        /// iOS ⽹网络延迟错误
        /// </summary>
        /// <param name="item">Item.</param>
        private void OnDeferred(Product item)
        {
            //Debug.Log("⽹网络连接不不稳");
        }
        /// <summary>
        /// 初始化失败
        /// </summary>
        /// <param name="error">Error.</param>
        public void OnInitializeFailed(InitializationFailureReason error)
        {
            isInitFailed = true;
            //Debug.Log("初始化失败");
            Debug.Log("IAPInitializeFailed!!!" + "Reason:" + error);
        }
        /// <summary>
        /// 恢复购买
        /// </summary>
        public void RestorePurchases()
        {
            if (Application.platform == RuntimePlatform.IPhonePlayer ||
            Application.platform == RuntimePlatform.OSXPlayer)
            {
                if (!isInited)
                {
                    //loading.SetActive(false);
                    InitPurchase();
                }
                StartCoroutine("InitAndRestore");
            }
        }
        IEnumerator InitAndRestore()
        {
            if (isInitFailed || !isInited)
            {
                //初始化失败
                StopCoroutine("InitAndRestore");
            }
            yield return new WaitUntil(() =>
            {
                return m_Controller != null &&m_AppleExtensions != null;
            });
            m_AppleExtensions.RestoreTransactions((result) =>
            {
                // The first phase of restoration. If no more responses are received on ProcessPurchase then
                // no purchases are available to be restored.
                Debug.Log("RestorePurchases continuing: " + result +". If no further messages, no purchases available to restore.");
                if (result)
                {
                    //产品已经restore,不不过官⽅方的解释是恢复过程成功了了,并不不代表所购买的物品都恢复了了
                }
                else
                {
                    // 恢复失败
                }
                StopCoroutine("InitAndRestore");
            });
        }
        /// <summary>
        /// 按钮点击  也可以重写为传入产品ID  此处是序列号
        /// </summary>
        /// <param name="index">Index.</param>
        public void OnPurchaseClicked(int index)
        {
            if (Application.platform == RuntimePlatform.IPhonePlayer ||
            Application.platform == RuntimePlatform.OSXPlayer ||
            Application.platform == RuntimePlatform.OSXEditor)
            {
                if (!isInited)
                    InitPurchase();
                StartCoroutine("InitAndPurchase", index);
            }
        }
        IEnumerator InitAndPurchase(int index)
        {
            if (isInitFailed || !isInited)
            {
                //初始化失败
                StopCoroutine("InitAndPurchase");
            }
            yield return new WaitUntil(() =>
            {
                return m_Controller != null && m_AppleExtensions != null;
            });
            m_Controller.InitiatePurchase(products[index].id);
            StopCoroutine("InitAndPurchase");
        }
        /// <summary>
        /// 购买成功回调
        /// </summary>
        /// <returns>The purchase.</returns>
        /// <param name="e">E.</param>
        public PurchaseProcessingResult
        ProcessPurchase(PurchaseEventArgs e)
        {
            //Debug.Log("购买成功回调");
            //使⽤用id判断是否是当前购买的产品,我这⾥里里只有⼀一个产品,所以就是products[0]
            if (e.purchasedProduct.definition.id == products[0].id)
            {
                //此处可以对订单进行验证,因为我们项目吧验证放到服务器了,所以我们可以把购买成功后的凭证发给服务器去验证
                //这个购买验证码特别特别长,验证分为沙盒验证和真实验证 就不赘述了
            }
            return PurchaseProcessingResult.Complete;
        }
        public void OnPurchaseFailed(Product i, PurchaseFailureReason p)
        {
            //购买失败的逻辑
            //Debug.Log("购买失败的逻辑");
        }
    
    }
    

    一些注释已经解释的很清楚了,这里解释一下Products 和PublicKey 这两个字段:在这里插入图片描述
    Products里面的id就是上面的产品ID,productType就是我们注册这个商品时的消耗类型,然后publicKey可以在上面的App专用共享秘钥查看,这个每一个app是固定不变的!然后我们只需要给我们的商品btn添加事件就可以了!

    测试

    我们可以做一个简单的测试工程,工程里不需要多只需要一个商品按钮就可以了,然后我们在打个ios包导入到我们苹果设备上(具体IOS打包请自行百度,其实和打android包差不多,只不过需要xcode编译一下,然后配置一下证书和访问权限就可以了),然后在手机上运行测试工程,这里有一个问题需要注意一下,由于我们是沙盒测试支付(是需要用沙盒账号去测试的),所以在进入工程前我们需要去我们的手机appstore上把我们的苹果账号登出,不然进入工程会默认登录的,然后进入我们的测试工程项目点击购买,项目有点简陋()
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    测试成功!!!以上就是沙盒环境下的苹果支付测试,有很多不足之处欢迎大佬指正!

    展开全文
  • iOS内购图文流程(2017)

    2017-06-22 13:06:00
    电子书......) 都需要走内购流程,苹果这里面抽走三成。 使用内购需要走的流程。 1,填写协议,税务和银行业务; 2,填写用户和职能; 创建内购的项目 3,写代码,和后台配合 4,添加项目内购测试账号;检验成果 具体步骤: 接...

    什么是内购?

    只要在iPhone App上购买的不是实物产品(也就是虚拟产品如qq币.虎牙币.电子书......) 都需要走内购流程,苹果这里面抽走三成。

    使用内购需要走的流程。

    1,填写协议,税务和银行业务;

    2,填写用户和职能; 创建内购的项目

    3,写代码,和后台配合

    4,添加项目内购测试账号;检验成果

    具体步骤:

    接下来的步骤特别繁琐,请做好准备。

    一.填写协议,税务和银行业务配置 https://itunesconnect.apple.com

    1030287-20170622125619241-55874283.png

    1.请求合同

    1030287-20170622125638070-543423047.png

    填写地址信息

    1030287-20170622125646460-1448782387.png

    阅读同意协议

    1030287-20170622125652726-1592458621.png

    2.填写联系方式

    1030287-20170622125657804-1553327892.png

    添加新的联系人

    1030287-20170622125707632-658514042.png
    1030287-20170622125755945-1745967390.png

    选中联系人

    1030287-20170622125805054-411099608.png

    3.填写银行信息

    1030287-20170622125813741-300490515.png

    添加银行卡

    1030287-20170622125820023-1297009354.png

    选择银行卡所在国家

    1030287-20170622125826648-2103370385.png

    ####填写银行CNAPS Code

    1030287-20170622125846116-674438845.png

    货币类型 CN银行账号如果是对公的账号,需要填写公司的英文名称,如果没有的话拼音

    1030287-20170622125853382-1203510180.png

    4.填写税务信息

    U.S Tax Forms: 美国税务
    Australia Tax Forms:澳大利亚税务
    Canada Tax Forms: 加拿大税务
    1030287-20170622125903976-775905449.png

    第一个问题如下:询问你是否是美国居民,有没有美国伙伴关系或者美国公司,如果没有直接选择No。

    1030287-20170622125920179-833560622.png

    第二个问题如下:询问你有没有在美国的商业性活动,没有也直接选No
    1030287-20170622125932273-1118143563.png

    然后填写你的税务信息,包括以下几点:

    Individual or Organization Name:个人或者组织名称
    Country of incorporation: 所在国家
    Type of Beneficial Owner:受益方式,独立开发者选个人
    Permanent Residence:居住地址
    Mailing address:邮寄地址
    Name of Person Making this Declaration:声明人
    Title:头衔

    填写完这些信息后就可以提交了

    1030287-20170622130011570-2075870248.png
    1030287-20170622130019070-1411236711.png
    1030287-20170622130029226-507295182.png

    5.等待审核,时间24小时左右。

    1030287-20170622130034835-1701073881.png

    二.创建内购项目

    选中你要添加内购项目的APP然后点击功能,点击+创建内购项目。

    1030287-20170622130043320-1377803478.png

    根据自己APP的需求选择类型

    1030287-20170622130107288-1299089719.png

    填写项目名称id (ps:应用在首次添加内购买项目时需要与新的应用版本一起提交审核。)

    1030287-20170622130113663-1049852533.png

    添加沙盒技术测试员

    用户职能>沙箱技术测试员

    1030287-20170622130118788-65907640.png

    1030287-20170622130126148-805316631.png

    注意事项:

    邮箱随便写但是格式要正确(test@163.com),但是不能用已经是AppleID的邮箱

    App Store 地区不要乱选。虽然随便哪个地区都可以用来测试(还没上线之前app并没有地区之分),但是在沙盒测试的时候,弹出的购买提示框会根据当前AppleID(沙盒账号)的地区显示语言的。

    使用沙盒测试一定要用真机 (越狱的不行)

    沙盒账号不能直接登录AppleStore登录时会提示"不允许创建iTunes账户”

    进行沙盒测试时需要退出AppleStore的账号

    三.代码

    #import <Foundation/Foundation.h>
    #import "WXApi.h"
    
    typedef NS_ENUM(NSInteger , Payment) {
        IAP6 = 6,
        IAP25 = 25,
        IAP60 = 60
    };
    
    @interface PayCenter : NSObject
    
    singleton_interface(PayCenter);
    
    - (void)startInit;
    
    - (void)payWithMoney:(NSInteger)money andType:(PayType)type dataDic:(NSDictionary *)dataDic;
    
    @property (nonatomic, strong) MBProgressHUD * hud;
    
    @property (nonatomic, copy) NSString * rechargeNumber;
    
    @end
    
    #define kProductID_IAP6 @"org.qqhl.nursning.3175"//6
    
    #import "PayCenter.h"
    #import "PayHttpRequest.h"
    //#import "Order.h"
    //#import "DataSigner.h"
    //#import <AlipaySDK/AlipaySDK.h>
    #import <StoreKit/StoreKit.h>
    
    
    @interface PayCenter()<SKPaymentTransactionObserver,SKProductsRequestDelegate>
    {
        int buyType;
        NSInteger _money;
    }
    @property (nonatomic, strong) NSMutableDictionary * dataDic;
    
    @end
    @implementation PayCenter
    
    singleton_implementation(PayCenter);
    
    - (void)startInit
    {
        [WXApi registerApp:kWeChatShareAppKey withDescription:appDefaultTilte];
    }
    
    - (void)payWithMoney:(NSInteger)money andType:(PayType)type dataDic:(NSDictionary *)dataDic
    {
        _money = money;
        self.dataDic = [NSMutableDictionary dictionaryWithDictionary:dataDic];
        switch (type) {
            case PayTypeWX:
                [self payTypeWXPay];
                break;
            case ApplePay:
    //            [self payTypeAliPay];
                [[SKPaymentQueue defaultQueue]addTransactionObserver:self];
                
                if ([SKPaymentQueue canMakePayments]) {
                    HULog(@"允许程序内付费购买");
    
                    [self payTypeApplePay];
                }else {
                    HULog(@"不允许程序内付费购买");
                    UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:@"提示"
                                                                        message:@"您的手机没有打开程序内付费购买"
                                                                       delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭",nil) otherButtonTitles:nil];
                    
                    [alerView show];
                    
                }
    
                break;
            default:
                break;
        }
    }
    - (NSString *)payTypeWXPay
    {
        __block NSString * str = @"";
        [PayHttpRequest payRequestWithType:PayTypeWX dataDic:self.dataDic success:^(BOOL success, NSDictionary *dic) {
            if (success) {
                if (dic != nil) {
                    NSDictionary * orderDic = [dic objectForKey:@"orderInfo"];
                    NSMutableString *stamp  = [orderDic objectForKey:@"timestamp"];
                    //调起微信支付
                    PayReq* req             = [[PayReq alloc] init];
                    req.partnerId          = [orderDic objectForKey:@"partnerid"];
                    req.prepayId            = [orderDic objectForKey:@"prepayid"];
                    req.nonceStr            = [orderDic objectForKey:@"noncestr"];
                    req.timeStamp          = stamp.intValue;
                    req.package            = [orderDic objectForKey:@"package"];
                    req.sign                = [orderDic objectForKey:@"sign"];
                    [WXApi sendReq:req];
                }else{
                    [[PayCenter sharedPayCenter].hud hide:YES];
                    str = @"服务器返回错误,未获取到json对象";
                }
            }
        }];
        return str;
    }
    - (void)payTypeApplePay{
        NSLog(@"---------请求对应的产品信息------------");
        NSArray *product = nil;
        switch (_money) {
            case IAP6:
            {
                product=[[NSArray alloc] initWithObjects:@"org.qqhl.nursning.6",nil];
                NSLog(@"---------IAP6------------");
    
            }
                break;
            case IAP25:
            {
                NSLog(@"---------IAP25------------");
    
            }
                break;
            case IAP60:
            {
                NSLog(@"---------IAP60------------");
            }
    
                break;
         
            default:
                break;
        }
        
        NSSet *nsSet = [NSSet setWithArray:product];
        SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:nsSet];
        request.delegate = self;
        [request start];
    }
    #pragma mark -  SKProductsRequestDelegate代理
    //返回的在苹果服务器请求的产品信息
    - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
        HULog(@"收到产品信息");
        NSArray *myProduct = response.products;
        HULog(@"产品id %@",response.invalidProductIdentifiers);
        NSLog(@"产品付费数量%d",(int)[myProduct count]);
    //populate UI
        for (SKProduct *product in myProduct) {
          NSLog(@"product info");
          NSLog(@"SKProduct 描述信息%@", [product description]);
          NSLog(@"产品标题 %@" , product.localizedTitle);
          NSLog(@"产品描述信息: %@" , product.localizedDescription);
          NSLog(@"价格: %@" , product.price);
          NSLog(@"Product id: %@" , product.productIdentifier);
        }
        SKPayment *payment = nil;
        switch (_money) {
            case IAP6:
            {
                payment = [SKPayment paymentWithProductIdentifier:kProductID_IAP6];
            }
                break;
            case IAP25:{
                
            }
            default:
                break;
        }
        
        NSLog(@"发起购买请求");
        [[SKPaymentQueue defaultQueue]addPayment:payment];
        
        
    }
    //实现监听方法
    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
        NSLog(@"调用了几次这个方法?");
        SKPaymentTransaction *transaction = transactions.lastObject;
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased: {
                NSLog(@"购买完成,向自己的服务器验证 ---- %@", transaction.payment.applicationUsername);
                NSData *data = [NSData dataWithContentsOfFile:[[[NSBundle mainBundle] appStoreReceiptURL] path]];
                NSString *receipt = [data base64EncodedStringWithOptions:0];
    //            [self buySuccessWithReceipt:receipt transaction:transaction];
            }
                break;
            case SKPaymentTransactionStateFailed: {
                NSLog(@"交易失败");
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            }
                break;
            case SKPaymentTransactionStateRestored: {
                NSLog(@"已经购买过该商品");
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            }
                break;
            case SKPaymentTransactionStatePurchasing: {
                NSLog(@"商品添加进列表");
            }
                break;
            default: {
                NSLog(@"这是什么情况啊?");
            }
                break;
        }
    }
    
    @end

    转载于:https://www.cnblogs.com/huanghaipo/p/7064383.html

    展开全文
  • Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程 这是前面的第一篇,详细的说明了怎样添加内购项目以及填写银行信息提交以及沙盒测试员的添加使用以及需要我们注意的东西,结果,被移除首页了!前面第一篇...
  • ​​通过这一改变,将极大程度上减少用户在使用 Touch ID 或 Face ID 时因为误触而出现内购的情况。实际测试兽兽在看到这个消息以后,马上亲自去试验了一下,结果却...​​打开某款 App 来订阅连续包月会员,使用 ...
  • 自然而然,iOS内购的功能是一定要有了(一次性消耗品只能用到内购,微信支付宝等支付是一定会被苹果拒的,与安卓开发不一样)。 下面,我会从头到尾的开始说内购的步骤,目前的状态是项目即将上线,沙盒测试环境...
  • 最近在做iOS内购,碰到的...描述可以写为,充值1元兑换100虚拟币二是因为苹果审核的流程,需要你提供虚拟货币的用处说明3、票据验证关于票据验证,是指你支付成功后,需要请求苹果的服务器去验证票据//沙盒测试环境...
  • iOS开发支付篇——内购(IAP)详解

    千次阅读 2018-10-23 12:02:31
    苹果客户端购买虚拟商品是需要走内购 先看gif图 具体步骤: 协议、税务和银行业务 信息填写 内购商品的添加 添加沙盒测试账号 内购代码的具体实现 内购的注意事项 流程 1.注册app,填写协议、税务和银行业务 注册...
  • iOS IAP应用购买汇总

    千次阅读 2015-06-26 17:06:57
    详解iOS应用程序使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程 http://www.himigame.com/iphone-cocos2d/550.html IAP 计费被拒绝解决方案 ... 关于IAP:苹果的审核规则发生了一点变化-
  • 目录(?)[-] 1 适用情况2 购买及发放虚拟产品流程3虚拟产品 虚拟产品的分类关于自动更新订阅品更新...填写银行卡与纳税信息新建虚拟产品新建测试帐号附在苹果托管不可消耗品Non-consumable products的内容需知 5 代码
  • 在维护老项目时,发现iOS12版手机上无法充值成功,经过测试发现,仅有iOS12版及以上版本手机上无法充值成功,卡在了向苹果发送产品ID后等待回调时,无回调!因为iOS11版没有问题,排除充值流程上的问题了,经过和能够在iOS12...
  • XCode发布App到调试机

    2015-06-09 22:55:05
    今天我们介绍如何用XCode发布我们开发的App到我们自己的机子上(如何发布到App Store不在本文讨论范围)。...注意在真机测试前,你必须先购买苹果的iOS开发者证书(99刀)。具体购买流程可以参考这里:http
  • XCode发布App到调试机 今天我们介绍如何用XCode发布我们开发的App到我们...注意在真机测试前,你必须先购买苹果的iOS开发者证书(99刀)。具体购买流程可以参考这里:http://blog.csdn.net/htttw/article/de...
  • iPhone开发秘籍(第2版)--源代码

    热门讨论 2012-12-11 13:51:22
     《iphone开发秘籍(第2版)》提供了关于iphone sdk以及iphone开发的全面信息,对iphone sdk中的各种组件做了深入浅出的介绍,包括iphone 3.0 sdk的所有新增特性,同时对iphone开发的基本流程、基本原理和基本原则...
  • 《iOS开发指南》(第二版 iOS7)源码

    千次下载 热门讨论 2014-03-20 11:11:59
    此外,还介绍了植入广告和应用购买的API,其中植入广告包括苹果自己的iAd和谷歌的AdMob广告。  第18章首先介绍了一些常用的调试工具、异常堆栈报告分析,接下来讲解了如何在真机上调试应用,最后分析了Xcode设备...
  • 此外,还介绍了植入广告和应用购买的API,其中植入广告包括苹果自己的iAd和谷歌的AdMob广告。  第18章首先介绍了一些常用的调试工具、异常堆栈报告分析,接下来讲解了如何在真机上调试应用,最后分析了Xcode设备...
  • IOS 由苹果公司开发的移动操作系统 Webkit 一个开源的浏览器引擎,在手机上的应用十分广泛 Webview WebView(网络视图)能加载显示网页,可以将其视为一个浏览器。它使用了WebKit渲染引擎加载显示网页 Activity ...
  • ✅ 21云盒子:最容易使用的云 - 自动化你的工作流程。代码构建,静态网页,Web应用发布,云数据库托管,SSL证书生成和维护,极速CDN,私有网络的一站式服务平台 ✅ BlinkMath:iOS app,累了眨眨眼,动动脑! 2020...

空空如也

空空如也

1 2
收藏数 24
精华内容 9
关键字:

苹果内购流程流程测试