Skip to content

IAP Developer Guide

jonghwihan edited this page Mar 29, 2021 · 10 revisions

< NOTE >

This documents are for ONE store IAP SDK v16(API v4). If you are looking for the latest version of ONE store IAP SDK v17(API v5), please refer to the url below:

最新版本的IAP SDK文档 : https://dev.onestore.co.kr/devpoc/reference/view/IAP_v17_cn


预先准备

SDK下载

ONE商店为开发者提供library、源代码、测试用APK。

指南文件可在网站确认,不另发布。 示例代码在GitHub共享,不另发布。

工程以branch区分,区分方式如下。

IAP library如果需要变更服务政策、新增功能或修补错误,可以进行非定期性更新。IAP库有更新,会在ONE商店开发者平台的公告栏提示相关信息,并向ONE商店开发者平台的开发者发送电子邮件进行公告。 开发者要开发APP,必须使用在ONE商店开发者平台的最新Library进行开发。否则无法操作应用内购买,并开发的应用无法注册于ONE商店开发者平台。

应用内商品注册

如果要使用IAP,必须有商品的AID和应用内商品ID,因此,开发者有必要提早注册欲在开发者平台销售的商品和应用内商品。应用内商品类型如下。

获取AID即可,不必提早上传APK文档。

ONE商店支付系统不同于谷歌IAB,必须提早区分应用内商品类型,并没有开发者直接消费(consume)处理概念。

区分 内容 备注
消耗性商品 可随时再购买或反复购买的商品 不消费也可以反复购买
永久性商品 只可购买一次,无法再次购买的商品
期间性商品 购买一次后,在特定期间内无法再次购买的商品 剩余期间可在SDK中通过查询确认
包月自动支付商品 每月自动付一定金额的商品,最长为一年 要解除该功能,可以在SDK通过查询变更有关状态

不建议使用正版切换商品

注册方法

应用内商品注册方法共有二种,为统一注册和各别注册。

应用内商品统一注册

应用内商品目录(CSV)可先在GoolePlay开发者凭条、NAVER应用商店开发者平台等下载,简单地编辑后统一注册。

  1. 在他公司开发者平台中下载应用内商品目录
  2. 下载ONE商店开发者平台的上传格式或者点击链接 链接 (图中①)
  3. 将他公司CSV文档复制后贴在格式中
  4. 将文档(Xls)上传于ONE商店开发者平台 (图中②)
  5. 确认所注册的应用内商品AID和商品ID

enter image description here

应用内商品各别注册

先登陆于ONE商店开发者平台,进入菜单中的【Apps(应用)】,点击商品注册后,上传商品。

enter image description here


【应用内商品信息】中注册相关商品。 [应用内商品信息]

在注册应用内商品之前,提早注册结算信息。

enter image description here


点击商品注册后,填写应用中要销售的商品信息。

  • 先在应用内商品标题中填写欲销售的商品标题,并选定相应商品的种类。
  • 应用内商品路径是以后在统一开发者平台验证时所必要的信息,填写相应商品路径即可。
  • 在应用内商品价格中填写包含增值税的商品价格。
  • 填写所有信息后,点击储存按钮。

enter image description here


确认应用ID(AID)和应用内商品ID(product_id)。

enter image description here

下载ONE商店及服务模型

只要将ONE商店客户端和服务模块安装于用户终端中,就可正常开启ONE商店的IAP第4版本。

执行ONE商店客户端,自动下载服务模块。


设计应用内支付模型

在适用IAP SDK之前,有必要设计相应模型。模型(Model)指的是包含以下内容的整体流程。

  • 通过IAP SDK购买(支付)
  • 进行购买(支付)时所需的认证
  • 应用内授权的方式

内建模型

在未链接应用服务器的前提下,如果想通过智能手机中安装的应用提供应用内商品时可使用该方式。

应用程序内建应用内商品,支付完毕后,应用内购买的应用内商品将会被解除锁定。

enter image description here

流程

  1. 应用呼叫IAP API后,请求支付。
  2. IAP Plug-in向IAP服务器请求支付,IAP服务器处理支付后,递交支付结果。
  3. IAP Plug-in将支付结果递交至应用。
  4. 允许已购买的应用内商品的使用 >【强力建议流程】3-1和3-2应用是在收取支付结果后,验证TxID电子发票,此后允许使用商品。

服务器模型

应用内商品支付完毕后,以应用(APP)连接至开发者应用服务器(App Server),请求应用内商品的使用权限,必要时可下载相应商品。

ONE商店为了让用户确认应用服务器使用权限,随时查询IAP服务器中历史购买内容,将提供TID购买记录查询API及 TxID电子发票验证API。

enter image description here

流程

  1. 应用呼叫IAP的API后,请求支付。
  2. IAP Plug-in向IAP服务器请求支付,IAP服务器处理支付请求后,将支付结果传送。
  3. IAP Plug-in将支付结果传至应用。
  4. 应用向应用服务器请求确认所购买的应用内商品授权。
  5. 应用服务器确认使用权限。
  6. 根据应用服务器的权限确认结果决定应用内商品是否可以使用>[强力建议流程(Flow)] > 5-1 应用服务器将会向 IAP服务器请求再次确认购买信息,此后允许应用内商品的使用

实现应用内支付(适用IAP SDK)

IAP SDK是在应用中为支持ONE商店支付功能的开发工具。 library有以下两种方式。(到目前为止,公司考虑提供aar形式)

  • Android Library (.aar)
  • Java Library (.jar)

以安卓用Java Library形式提供,结构如下。

iap_plugin 文件夹
    L iap_plugin_[version].aar
    L iap_plugin_[version].jar

详情参见引用示例代码


Library配置

包含在IAP SDK中的Library将配置于以下位置。

ex: [Project文件夹]/app/libs/

设置在创建脚本(ex: app/build.gradle)里包含着Library。

  • Java Library (.jar)
dependencies {
        compile files('libs/iap_plugin_[版本]_[创建日期].jar')
}
  • Android Library (.aar)
allprojects {
    repositories {
        flatDir {
            dirs 'libs'
        }
    }
}

dependencies {
        compile(name:'iap_plugin_[版本]_[创建日期]', ext:'aar')
}

AndroidManifest.xml 配置

为了正常启动IAP组件,应在 AndroidManifest.xml 文件中配置IAP版本信息。如果不配置以下内容,应用无法正常启动,并无法注册应用内商品。

新版不必在AndroidManifest.xml声明权限。 并且,SDK原本需要运行时权限(Runtime Permission)对应,但这里提供的SDK不必另行处理。

iap:api_version设置

应用必须设置所有列出的meta-data。name值是固定的,但value值是根据API版本变动。

若是16.XX.XX版本,输入方法如下。

<application ...... >
   <meta-data
      android:name="iap:api_version" 
      android:value="4" />

斟酌orientation的变更

请求支付或查询(显示界面时)时,则显示有portrait属性的支付Activity。

如果请求支付的Activity的属性为landscape,则发生系统环境变化,在无特别设置的情况下,有可能呼叫生命周期函数(如,onDestroy)。

如果在Activity中再创建IapPlugin实例(instance)和回调实例,有可能无法正常收到回调。

较为简单的解决方法有使用IapPlugin的Activity配置configChanges值,具体方法如下。

<activity 
    ...
    android:configChanges="orientation|screenSize" />

关于包的说明

IAP SDK主要提供三个包。现在只用com.skplanet.dodo包,可实现所有功能。旧版SDK为了帮助实现功能,另外提供了helper包和pdu包。但,从API_VERSION 4开始不需要别个包。鉴于兼容性,加强了安全性,将这些功能都包含在SDK里面。

包名 说明
com.skplanet.dodo 有为实现功能的必选类
com.skplanet.dodo.helper 旧版所提供的内容
com.skplanet.dodo.pdu 旧版所提供的内容

关于类的说明

IAP SDK采用异步传送模式,先向IapPlugin instance发送请求,然后通过listener获得结果。 根据应用内商品的购买请求和回应结果而需要的适当处理方法,应该由开发者自定义实现。


关于IapPlugin类的说明

该类可发送所要的请求。可以有多数对象,各对象都被设置Development/Release。

使用结束后,要呼叫exit()方法,以后向IapPlugin发送请求的话,会发生运行时异常(runtime Exception)。


/**
 * 创建为请求的 IapPlugin对象
 */
IapPlugin getPlugin(Context context, String pluginmMode)

...

// 调试用
//mPlugin = IapPlugin.getPlugin(activity, IapPlugin.DEVELOPMENT_MODE);

// 支付请求(建议)
mPlugin = IapPlugin.getPlugin(activity, IapPlugin.RELEASE_MODE);

/**
 * 支付请求(建议)
 */
String sendPaymentRequest(IapPlugin.RequestCallback requestCallback, PaymentParams params)


/**
 * 支付请求
 */
Bundle sendPaymentRequest(String appId, String pId, String productName, String tId, String bpInfo, IapPlugin.RequestCallback requestCallback)

/**
 * 购买记录查询
 * 可查询单项、多数、全部商品
 */
String sendCommandPurchaseHistory(IapPlugin.RequestCallback requestCallback, ProcessType type, String appId, String... productIds)

/**
 * 获取商品信息
 */
String sendCommandProductInfo(IapPlugin.RequestCallback requestCallback, ProcessType type, String appId)

/**
 * 确认是否可购买
 * 可查询单项、多数商品。
 */
String sendCommandCheckPurchasability(IapPlugin.RequestCallback requestCallback, ProcessType type, String appId, String... productIds)

/**
 * 变更已购买的商品状态
 * 若是包月型商品,将取消服务,若是消耗性商品,则请求扣除积分等
 */
String sendCommandChangeProductProperties(IapPlugin.RequestCallback requestCallback, ProcessType type, String appId, String action, String... productIds)

/**
 * 为验证电子发票的函数
 */
void sendReceiptVerificationRequest(String appId, String txId, String signData, ReceiptVerificationTask.RequestCallback callback)

/**
 * 结束函数
 * 以后不可请求(发生 IllegalStateException )
 */
void exit()

IapPlugin.RequestCallback接口


public interface RequestCallback {
    void onResponse(IapResponse data);
    void onError(String reqid, String errcode, String errmsg);
}

对请求(查询或支付)的回调应该是实现相应接口的。

不建议自行实现接口,建议使用IapPlugin.AbsRequestCallback。


IapPlugin.AbsRequestCallback抽象类

该抽象类不仅实现 IapPlugin.RequestCallback 接口,而且还能接收到请求结果。如果开发者自行实现接口,以String全文获得“json”结果,需要各别实现解析(Parsing)。

但是如下实现IapPlugin.AbsRequestCallback抽象类,请求(查询或支付)时作为参数传递,将会收到数据类形式的结果。

以弱引用(WeakReference)来管理Listener,因此,在方法中不可创建匿名类(anonymous class)传送。(从SDK v16.03.00 变更为强引用(StrongReference)则可以忽略。)


private IapPlugin.AbsRequestCallback mAbsRequestCallback = new IapPlugin.AbsRequestCallback() {
        @Override
        protected void onResponse(Response response) {
            // 正常接收回应
        }

        @Override
        public void onError(String reqid, String errcode, String errmsg) {
            // 发生错误
        }
    };

此为RequestCallback接口的onError() 方法,传递错误代码和错误信息。

onError错误代码和信息定义请见引用

  • reqid : 请求ID
  • errcode: 错误代码
  • errmsg: 错误代码

Response类

此为Response类(以对象形式存有结果)。IapPlugin.AbsRequestCallback的onResponse呼叫(回调)时作为参数传送。

I该类将从服务器收到的String形式的结果切换为Data class形式。However, a modification is made in the version (16.xx.xx) onward to 开发者使用旧版时,通过样式工程的helper包自行切换,但新版被修改为先切换后传输结果。 所有Field作为public直接切入。

所有Field有可能是null。从服务器中未收到信息的部分都基本上设为null。


public class Response {
    public final String api_version;
    public final String identifier;
    public final String method;
    public final Result result;
    ...
}

public static class Result {
    public final String message;
    public final String code;
    public final String txid;
    public final String receipt;
    public final Integer count;
    public final List<Product> product;
    ...
}

public static class Product {
    public String appid;
    public String id;
    public String name;
    public String type;
    public String kind;
    public Integer validity;
    public Double price;
    public String startDate;
    public String endDate;
    public Boolean purchasability;
    public Status status;
    ...
}

public static class Status {
    public String code;
    public String message;
    ...
}

ProcessType Enum说明

定义请求处理类型的enum。


public enum ProcessType {
    BACKGROUND_ONLY,
    FOREGROUND_IF_NEEDED,
}

建议设为FOREGROUND_IF_NEEDED。 请求处理过程中需要显示UI的部分如下:

  • 请求权限画面
  • 请求ONE Store Service(OSS)安装画面 如果想要中断以上状况时的请求,并接收错误信息,可用BACKGROUND_ONLY发送请求。

Example(请求)

  • 初始化

// IapPlugin 引用本文
private IapPlugin mPlugin;

// 对收到请求结果(查询和支付)类的声明和创建
private IapPlugin.AbsRequestCallback mAbsRequestCallback = new IapPlugin.AbsRequestCallback() {
	@Override
	protected void onResponse(Response response) {
		// 回应
	}

	@Override
	public void onError(String reqid, String errcode, String errmsg) {
		// 错误
	}
};

...

// 为请求而创建对象,并储存引用
mPlugin = IapPlugin.getPlugin(activity, IapPlugin.RELEASE_MODE);

  • 请求支付

//为支付而创建 PaymentParams对象
PaymentParams params = new PaymentParams.Builder("OA00123456", "0910012345").build();

...

// 支付请求
mPlugin.sendPaymentRequest(mAbsRequestCallback, params);

  • 购买记录查询

mPlugin.sendCommandPurchaseHistory(mAbsRequestCallback, ProcessType.FOREGROUND_IF_NEEDED, "OA00123456", "0910012345");

  • 获取商品信息

mPlugin.sendCommandProductInfo(mAbsRequestCallback, ProcessType.FOREGROUND_IF_NEEDED, "OA00123456");

  • 确认是否可购买

mPlugin.sendCommandCheckPurchasability(mAbsRequestCallback, ProcessType.FOREGROUND_IF_NEEDED, "OA00123456", "0910012345");

  • 变更已购买的商品状态

mPlugin.sendCommandChangeProductProperties(mAbsRequestCallback, ProcessType.FOREGROUND_IF_NEEDED, "OA00123456", Action.cancel_subscription.action(), "0910012345");

  • 为电子发票验证的函数

//对收到请求结果(电子发票验证)类的声明和创建
private ReceiptVerificationTask.AbsRequestCallback mRvRequestCallback = new ReceiptVerificationTask.AbsRequestCallback {
  
    @Override
    protected void onResponse(VerifyReceipt response) {
    	// 回应
    }

    @Override
    public void onError(int code) {
    	// 错误
    }
};
    
...

// 请求电子发票验证
mPlugin.sendVerifyReceiptRequest(mRvRequestCallback, response.result.txid, "OA00123456", response.result.receipt);


Example(回应)

  • • 支付回应

@Override
protected void onResponse(Response response) {
	// 成功代码
	final String successCode = "0000";	
	
	// 确认成功
	if (successCode.equals(response.result.code)) {
		// 支付成功
	}
}

  • 购买记录查询

@Override
protected void onResponse(Response response) {
	final String successCode = "0000"; // 查询成功代码
	if (successCode.equals(response.result.code)) {
		// 特定商品购买记录查询
		// 结果中有多个商品,获得了所需的商品
        for (Response.Product p : response.result.product) {
            // 确认商品认证是否成功
            final String successStatusCode = "PH00";
            if (successStatusCode.equals(p.status.code)) {
                //在Product p中获得所需的商品
            }
        }
	}
}

  • • 获取商品信息

@Override
protected void onResponse(Response response) {
	final String successCode = "0000"; // 查询成功代码
	if (successCode.equals(response.result.code)) {
		// 确认特定商品信息
		// 结果中有多个商品,获得了所需的商品
        for (Response.Product p : response.result.product) {
	        // 在Product p获的了所需的信息
        }
	}
}

  • 确认是否可购买

@Override
protected void onResponse(Response response) {
	final String successCode = "0000"; // 查询成功代码
	if (successCode.equals(response.result.code)) {
		if (response.result.product != null && response.result.product.size() > 0) {
            Response.Product p = response.result.product.get(0);
			if (p.purchasability) {
				// 可购买
            }
        }
	}
}

  • 变更已购买的商品状态

@Override
protected void onResponse(Response response) {
	final String successCode = "0000"; // 查询成功代码
    if (successCode.equals(response.result.code)) {
        if (response.result.product != null && response.result.product.size() > 0) {
            Response.Product p = response.result.product.get(0);
            final String successStatusCode = "CS00"; // 商品状态变更成功结果代码
            if (successStatusCode.equals(p.status)) {
                // 取消自动支付商品
            }
        }
    }
}


@Override
protected void onResponse(Response response) {
	final String successCode = "0000"; // 查询成功代码
    if (successCode.equals(response.result.code)) {
        if (response.result.product != null && response.result.product.size() > 0) {
            Response.Product p = response.result.product.get(0);
            final String successStatusCode = "SP00"; // 商品状态变更成功的结果代码
            if (successStatusCode.equals(p.status)) {
                // 扣除道具成功
            }
        }
    }
}

  • 为电子发票验证的函数

@Override
public void onResponse(final VerifyReceipt verifyReceipt) {
    // 成功代码
    final Integer successStatusCode = 0;
    final String successDetailCode = "0000";
    if (successStatusCode == verifyReceipt.status && successDetailCode.equals(verifyReceipt.detail)) {
        if (verifyReceipt.product != null && verifyReceipt.product.size() > 0) {
            VerifyReceipt.Product p = verifyReceipt.product.get(0);
            // 在Product p中获得了所需的信息
        }
    }
}


应用内支付测试

应用适用完IAP SDK后,在申请审核之前,必须进行自行测试进程。只要是通过正常性测试而产生的付费记录和购买记录,开发者平台就能进行审核,让开发商上架商品正式销售。

要附加进行商用服务器的测试,有各种限制。 因此,测试完毕后,只切换为商用服务器模式,也能进行审核申请。

注册测试用终端

【商品现状】页面中,进入【应用内商品信息】后,点击【测试】按钮。

enter image description here

点击【测试】将跳出【自行测试】提示窗。进入【测试终端】,注册要测试的终端MDN和测试用T商店的T币。只有用【测试终端】中的注册的测试编号,才能进行测试。

enter image description here

自行测试的强行设置 境外开发商或使用ONE商店不支持的手机用户,使用“IAP Setting APP”就能进行正常性测试。请下载上面的应用,根据以下说明进行设置 Download the App above, and set up the App following the instructions below.

下载IAP设定应用

  1. 请选择“Enable Setting”。
  1. 选定“移动通信公司(Carrier)”并输入手机号码(MDN)。
  2. 请按下“Save”按钮。
  3. 将在“Setup State”的“Foreign dev mode”设为“true”。
  4. 关掉设置应用,然后进行测试。

应用内商品测试的设置

自行测试提示窗中【各别商品设置】选项将显示要测试的应用内商品列表。在列表中选择要测试的一个应用内商品,这时将跳转到各别应用商品测试环境设置页面。只点击应用内商品提标则跳转到相应页面。

enter image description here

可在应用内商品测试环境设定页面中选择多种情况的支付进行测试。选择【结果设置】中所显示的任意一值并点击【储存】时,页面将移动至之前应用内商品的目录页面。

只有点击了【结果设置】中的“正常”才会付款,若选择其他则不会付款。

enter image description here

进行测试

执行已开发完毕的应用后,购买结果设置完毕的应用内商品,并进行测试。测试成功, 相应应用内商品测试结果则变更为“Y”。

enter image description here

付款查询

在【自行测试】提示窗的【付款记录查询】中,可以根据在上一步设置的测试环境确认测试结果。【结果设置付款结果】里显示在上一步设置的“结果设置”值和付款结果,比如“成功”或“失败”。

enter image description here

如果选择付款结果为“失败”的付款记录,将跳转到如下详细页面,可查看失败原因。

enter image description here

商用测试

根据商品的状态,有时可进行商用测试,有时不可进行商用测试。

  • 注册中 : 不可测试,与应用SDK连接的过程中会发生“-1001”错误 请见结果码引用
  • 审核中
    • 第一次审核:不可测试(没有销售中的商品)
    • 应用更新审核:只对销售中的商品可以进行测试
  • 销售中 : 可以测试

申请审核

IAP商品必须通过ONE商店开发者平台审核过程,并进行商用测试,才能发布销售。开发者在自行测试完毕后,将应用设置方式从开发用服务器改为商用服务器,申请审核业务。

应用内商品测试结果都要“Y”,如果有不是“Y”的商品,审核申请按钮不会激活。

详情请见商品注册指南 IAP Developer Guide

// 调式用
//mPlugin = IapPlugin.getPlugin(activity, IapPlugin.DEVELOPMENT_MODE);

// 商用
mPlugin = IapPlugin.getPlugin(activity, IapPlugin.RELEASE_MODE);

应用内支付退款

用户对已经购买的应用内商品,因道具未发放等理由,请求取消购买时,其取消步骤如下。

    1. 用户向开发商或ONE商店客户中心申请取消购买
    1. 开发商在开发者平台确认支付记录后,申请取消购买
    1. ONE商品有关部门确认此案后进行取消购买

确认客户购买记录的方法如下。

    1. 开发者平台 > 客户支援中心 > TxID购买记录管理
    1. 开发者平台 > 直接沟通 > 客户VOC>详细查询(弹窗)

详情请见 客户管理指南

Clone this wiki locally