原生双端淘宝客APP源码拆解:淘宝联盟API与代理分佣系统设计
2026/9/15 5:49:27 网站建设 项目流程

简介:淘宝客原生双端APP源码搭配代理系统,是市面上少见的移动端淘客项目资源。面向想搭建自有淘宝客应用的开发者、产品运营或创业者,适合有一定Android/iOS基础、希望直接获取完整工程源码并快速上手的用户。压缩包共2045个文件,约704.64MB,文件类型涵盖734个Java、717个XML、284个JSON、92个M与89个H等;其中Java与XML构成安卓客户端主体,JSON多用于接口数据配置,M与H对应iOS端源码,整体结构完整,另有txt、properties、md等说明文档。资源配有清晰安装教程及多份docx开发文档、接口文档、账号替换说明和申请材料清单,便于按步骤完成环境配置与上线发布。已有131人学习下载,适合需要参考原生APP架构、UI界面与代理系统联动逻辑的淘客从业者,可据此节省从零开发成本,快速搭建自己的推广分发平台。

1. 原生双端淘宝客APP源码,为何比WebView套壳更值得拆

把“淘宝客APP源码”丢进搜索框,能翻出来的工程十个里有九个是WebView套壳,壳子包一个H5页面,改个URL就算新版本。这套不一样。700多MB的交付体积,Android和iOS各一套原生工程,接口文档迭代到v1.45.1版本,代理系统和安装教程一并给出。它不是开箱即用的SaaS,需要你有Android或iOS的编译能力,但胜在完整——UI原生绘制、网络层真实请求、代理分佣逻辑齐全。适合拿原生工程做二次开发的技术团队,也适合想研究淘宝联盟OpenAPI在移动端如何被客户端拉起来的开发者。下面按工程结构、API调用链、代理系统、账号替换与部署几个方向拆开聊。

2. 双端工程结构与淘宝联盟API v1.45.1的调用链

2.1 原生工程里,先看network模块而不是Activity

解包Android端源码后,不要急着点开MainActivity,先看工程根目录的module划分。原生双端项目的标准做法是把代码拆成common(公共基础库)、network(网络层)、widget(自定义控件)、module-user(用户模块)、module-order(订单模块)这几块。这是一套主干分层,原生APP最忌讳Activity里堆一万行业务代码,工程规范度第一眼从模块划分就能判断。这套源码能看出同样的分层习惯,common里放工具类和Base基类,network独立成模块,页面模块依赖network模块而不是反着来。

先说看network模块的理由。淘宝客APP本质上是淘宝联盟OpenAPI的移动端客户端,商品列表、订单状态、佣金金额都要通过接口拉取。network模块里包含着Retrofit/OkHttp的封装方式、签名拦截器、加密参数处理逻辑,这些是淘宝客源码和普通APP源码差异最大的地方。iOS端对应的位置是APIManager目录,同样把网络请求收敛在一个核心类里。我见过不少二次开发的人直接绕过network模块,在页面里重写一个请求,结果签名参数缺失或渠道参数丢失,线上收不到佣金账单,排查时全部乱套。所以第一个建议是:改动前把network模块当成本工程的地基,不要在它之外另起网络请求。

2.2 商品、订单、授权三类接口的映射关系

v1.45.1的接口文档结构很清晰,把接口按业务拆成三类:商品信息类、订单查询类、授权绑定类。理解这三类的落点,客户端代码就找到路标了。下面的映射表是我对照文档和工程代码整理出来的,做二次开发时直接拿这张表当索引,比在工程里一个个翻方法快得多。

接口分类客户端对应模块返回数据典型用途
商品信息类首页推荐、搜索、商品详情商品标题、券后价、佣金率展示
订单查询类个人中心-订单列表、结算页预估佣金、订单状态、结算时间
授权绑定类登录注册、绑定上下级代理生成推广位、关联代理关系

这套映射关系在双端实现上是对齐的:Android端所有接口请求都收敛在DataRepository类里,iOS端对应APIManager层。新加页面时,理论上只需要调用仓库类暴露的方法,不需要各处散写请求代码。如果你从文档里看到一个新接口,第一件事就是在DataRepository里找有没有对应方法,没有的话按现有风格补一个,而不是直接在页面里写Retrofit调用。这个习惯能保证后续签名逻辑和埋点逻辑不会被绕过去。

2.3 签名拦截器的实现与时间戳坑点

淘宝联盟这类平台接口对签名要求严格,这套源码在Android端的实现方式是拦截器统一处理。我摘一段重构过的核心逻辑:

class TbkApiInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val original = chain.request() val ts = System.currentTimeMillis() / 1000 // 附加时间戳、签名和渠道标识,缺一服务端直接拒绝 val signedUrl = original.url.newBuilder() .addQueryParameter("timestamp", ts.toString()) .addQueryParameter("sign", genSign(original, ts)) .addQueryParameter("channel", "android_double_side") .build() return chain.proceed(original.newBuilder().url(signedUrl).build()) } private fun genSign(request: Request, ts: Long): String { // 从安全存储读取 app_secret,参数排序后拼接做MD5 val secret = KeyStoreHelper.get("tbk_secret") val pairs = request.url.queryParameterNames.sorted() .map { it to request.url.queryParameter(it) } val raw = pairs.joinToString("") { "${it.first}${it.second}" } + ts return MD5Util.md5(raw + secret) } }

这段代码做了三件事:第一,在每个请求上自动附加时间戳timestamp,服务端用它校验请求时间是否在允许窗口内;第二,按参数名排序拼接后加上app_secret生成签名,保证参数不被篡改;第三,追加渠道号channel,方便服务端按渠道统计App来源。如果服务端升级为HMAC-MD5或者RSA签名,也只需要替换genSign内部实现,对调用方无感知。

最容易被忽略的是时间戳。手机本地时间和服务器时间偏差超过5分钟,接口会直接返回签名错误。这种报错不是签名算法的问题,而是时间同步问题。常见做法是在冷启动时从服务器时间接口校准一次本地时间偏移量,后续所有请求时间戳都加上这个偏移。另一个坑是queryParameterNames在URL有重复参数时会丢值,如果有相同参数名以不同值出现,要改成手动解析原URL的query字符串。iOS端封装思路一致,只是用NSURLSession的代理方法统一附加参数。

3. 代理系统设计:分佣等级、结算链路与数据库落表

3.1 代理等级怎么定,决定了分佣比例的边界

代理系统是这套源码里最贴近商业逻辑的部分,模型很直接:用户通过代理的推广链接或二维码下载App并产生购买,平台从淘宝联盟拿到的佣金按比例分给代理,平台自己留一部分作为运营成本。这里最关键的是等级体系——不同等级对应不同分佣比例,也是代理长期推广的动力来源。等级定得不好,要么平台利润被吃掉,要么代理觉得赚不到钱而流失。

源码配套的开发文档对等级模型的说明很明确,我把它转成了一张可以直接落库的等级表:

CREATE TABLE `agent_level` ( `id` tinyint NOT NULL AUTO_INCREMENT, `level_name` varchar(32) DEFAULT NULL COMMENT '等级名称:普通代理/金牌代理/合伙人', `commission_rate` decimal(5,2) DEFAULT NULL COMMENT '代理可分成比例,0到1之间', `invite_required` int DEFAULT '0' COMMENT '需要邀请的有效下级数', `order_required` int DEFAULT '0' COMMENT '需要完成的累计有效订单数', `wx_withdraw_limit` decimal(10,2) DEFAULT '0.00' COMMENT '单次提现最小金额', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

几个字段的设计点值得注意。commission_rate存的是代理拿走的比例,例如0.70表示这单佣金里代理拿70%,平台留30%,比例一定控制在0到1之间,后台配置时做好范围校验。invite_required和order_required是升级条件,两个条件同时满足才会触发升级,这是防止“邀了一堆僵尸号就升金牌”的常用设计。wx_withdraw_limit是单次提现门槛,低于该金额不允许发起提现,目的是减少小额提现带来的手续费损耗。这套字段设计可以直接挪用到其他返利类项目里。

3.2 订单归因与回传的时序问题

代理推广的核心问题是:一笔订单到底算谁的。常规做法是,用户第一次点代理的链接时,客户端带着代理的PID去调用绑定关系接口,把用户和代理的绑定关系记在后台,之后用户一段时间内下的订单都归这个代理。这里有三个容易踩的坑。

第一,绑定关系必须只建立一次。代码里要做幂等处理,同一个用户重复打开不同代理的链接,以第一次绑定为准,后面只更新不覆盖。用数据库唯一索引来控制是兜底方案:

ALTER TABLE `user_agent_relation` ADD UNIQUE KEY `uk_user_agent` (`user_id`, `agent_id`);

第二,订单回流有延迟。淘宝联盟的订单查询接口一般T+1才返回完整佣金数据,当天显示的金额是预估,不是最终结算。客户端如果直接展示实时佣金,到账后对不上账,客诉就来了。我一般会把客户端文案定为“预估佣金”,后台只在状态变为已结算时才累加可提现余额。

第三,代理等级在订单产生后也可能变化。如果用户在A等级时下单,付款时升到了B等级,佣金按哪个等级算?业界通用做法是按下单当日的等级结算,而不是按付款或确认收货时的等级。这样对用户和平台都公平,也不会因为等级变化引发退款纠纷。

3.3 佣金结算批量处理的实现与参数解释

结算逻辑我按源码的文档思路写一个统计伪代码,方便理解整个链条:

from decimal import Decimal def settle_daily_orders(agent, orders): # 每天跑一次,把所有已确认收货订单算出来 gross = Decimal('0.00') settled = [] for order in orders: # pub_share_fee 是淘宝联盟实际结算的佣金总额 share_fee = Decimal(order['pub_share_fee']) rate = agent.level.commission_rate platform_fee = share_fee * Decimal('0.04') # 平台运营成本固定抽4% agent_income = share_fee * Decimal(str(rate)) - platform_fee if agent_income < 0: agent_income = Decimal('0.00') settled.append({ "order_id": order["order_id"], "agent_id": agent.id, "amount": agent_income.quantize(Decimal("0.01")), "status": "pending_withdraw", }) gross += agent_income return settled

为什么单拆一个platform_fee:代理拿的比例基于淘宝联盟结算的佣金总额,但平台本身有服务器成本、证书成本、运营人工,不做抽成一个月下来大概率是负利润。固定抽4%是比较常见的做法,也有人按代理等级递减,这是运营策略,改代码里的系数就行。max兜底负数,防止佣金率配置错误导致负数入账。这里有一个经验:金额计算一律用Decimal,不要用float,float的二进制精度问题在金额累加时会放大到分,对账时候非常头疼。

结算跑批建议放在凌晨淘宝联盟订单数据回传稳定之后,用定时任务触发。注意跑批时要加分布式锁或数据库行锁,避免同一笔订单被重复结算,重复结算一次,财务那边就得人工冲账一次。

3.4 提现记录与账务一致性

结算完成之后,还要有一张提现记录表。这里最容易被忽略的是账单号字段:每次提现应该生成全局唯一的流水号,同时关联原始订单明细。用户看到的是提现记录,财务看的是订单明细,两者对不上就出大事了。如果后台用PHP源码来写,这种结算模块尤其要提前把事务边界定义清楚,把入账、提现、手续费三条记录放到同一个事务里提交。

对账SQL是最常用的排查手段,后台如果接MySQL,可以这么查:

SELECT DATE(settlement_date) AS day, SUM(amount) AS total_income, COUNT(*) AS order_cnt FROM agent_settlement WHERE agent_id = 123 AND settlement_date >= '2024-06-01' GROUP BY DATE(settlement_date) ORDER BY day DESC;

按日期分组之后,如果平台后台的金额和淘宝联盟后台的金额对不上,逐日缩小范围查订单,这是最直接的排查路径。还有一种情况是两个平台显示的时间口径不同,一个按下单时间,一个按确认收货时间,对账时先把两边的时间字段统一后再比对。

4. Android账号替换、签名构建与安装部署全流程

4.1 替换账号前,先准备这些申请材料

安装教程文档里第一批强调的是“替换账号”,原因是源码里默认带的AppKey和PID不能直接商用,必须换成你自己的淘宝联盟账号。账号替换之前,需要备齐下面这些申请材料:

材料说明来源
淘宝联盟账号用于登录联盟后台,创建媒体备案淘宝或支付宝账号实名
AppKey / AppSecret创建应用后获取,接口调用的唯一凭据开放平台应用管理
媒体备案ID备案通过后获取,绑定推广位的前提联盟后台-媒体备案
推广位PID格式形如mm_123_456_789创建媒体后自动生成

材料清单在源码附带的《淘宝客客户需要申请材料》文档里写得很全,照着清单一项项办就行。以Android端为例,这些参数在工程里的位置一般在网络层,可能是res/values下的一个XML或者assets下的配置文件,源码文档里专门有一份《安卓淘宝客APP相关账号替换》说明,顺着它改就可以。关键点是AppSecret不能硬编码在Java代码里,最低限度也要放到加密存储类包一层,这一点在提交应用市场审核时是重点核查项。

4.2 账号配置与替换操作步骤

第一步,打开Android工程的配置文件,把AppKey、AppSecret、PID替换成自己的:

<!-- res/values/tbk_config.xml --> <resources> <string name="tbk_appkey">已申请的AppKey</string> <string name="tbk_secret">已申请的AppSecret</string> <string name="tbk_adzone_id">推广位ID</string> <string name="tbk_pid">mm_111_222_333</string> </resources>

第二步,如果之前运行过签名过的安装包,替换账号后建议卸载旧版本再安装。账号信息会缓存在SharedPreferences里,不卸载直接覆盖安装,旧账号的缓存可能覆盖掉新配置。这一步在安装教程里没有特别强调,但实际部署时踩过太多次,直接卸载重装是成本最低的验证方式。

第三步,检查权限配置。Android 12及以上版本,安装来源管理和文件访问权限需要在系统设置里手动开启。应用中如果包含版本升级下载功能,要主动引导用户跳转系统原生设置页:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); intent.setData(Uri.parse("package:com.tbk.app")); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); }

这里使用ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION而不是直接写死代码打开某个厂商ROM的设置页,是为了适配不同厂商的Android 11+系统。部分国产ROM对权限管理做了定制,标准Intent可能会失效,这种情况要再包一层异常捕获,兜底跳转到App详情页的ACTION_APPLICATION_DETAILS_SETTINGS。如果目标是小米、OPPO这些机型,测试时必须真机跑一遍这个流程,模拟器上是测不出问题的。

4.3 构建签名与安装命令

双端源码里Android端用Gradle构建,直接执行:

# 在android目录下执行 ./gradlew clean assembleRelease # 无签名信息的话,生成release包后再手动签名 jarsigner -verbose -keystore tbk.keystore \ -storepass 你的密码 -keypass 你的密码 \ -signedjar tbk_signed.apk \ app-release-unsigned.apk tbk_alias # 对测试机安装 adb install -r tbk_signed.apk

这里有几个容易踩的点。第一,Gradle构建时如果报Invalid signature,先检查Gradle版本与JDK版本是否匹配,用JDK 17构建老工程经常遇到这个问题,把JDK切换到11或8可以快速绕过。第二,jarsigner和apksigner两种签名工具选一个就好,优先用apksigner,它兼容新的签名方案v2/v3,提交到应用市场时不会被要求重新签名。第三,release包构建完成后,用aapt dump badging命令确认包名和版本号没问题再安装,不要拿一个配置错误的包做全量测试。

部署到服务器端的代理系统,如果团队拿到的是PHP源码实现,直接把代码包解压到Web目录,配置伪静态规则和数据库连接即可。这里多说一句:后台和App之间的接口通信建议加一层sign验证,否则代理后台的提现接口被遍历调用,是这类源码最常见的安全漏洞。

5. 上线前的实战自检:接口联通性与佣金链路闭环验证

5.1 三分钟接口状态验证

正式上线前,先用最直接的方式验证接口可用性,不要等用户反馈问题。在Android端跑一遍完整请求链路,打开Logcat过滤TbkApi,观察三类请求是否都返回HTTP 200。检查优先级如下表,按顺序一项项过:

检查项预期结果失败时的常见原因
AppKey / AppSecret 是否还有效无401账号续约过期,或密钥被误删
PID与媒体备案是否对应无业务错误码PID未通过媒体备案
时间戳误差无签名错误手机时间不准,未做时间偏移校准
订单接口返回数据有新增订单数据结算期间拉取失败,隔几分钟重试

5.2 佣金链路闭环验证

选一个测试账号,模拟完整的推广到结算流程。假设代理A邀请用户B购买一件商品。步骤是:登录后台,确认A和B的绑定关系已经写入user_agent_relation表;等淘宝联盟T+1回流订单,查询agent_settlement表是否有A的入账记录;检查A的累计收入、分佣比例、平台抽成三个数是否符合预期。手工验证时有个细节:用转链接口生成测试链接商品,不要拿已成交过的商品做验证,否则订单归因可能命中历史绑定关系,导致看不出问题。

如果测试中发现绑定关系没建立,第一反应是检查淘宝联盟的授权接口是否有重复回调问题——用户点链接后拉起的授权WebView如果被重复回调,后端可能把已绑定关系覆盖成新代理。解决思路维持“第一次绑定为准”原则,业务层加逻辑判断,数据层加唯一索引,双保险。

最后送一个排查技巧:当用户反馈“订单没佣金”时,优先查后台日志里淘宝联盟接口的授权token是否过期,而不是查代码逻辑。这类平台接口的token有效期通常只有几天,过期次数比代码bug高一个数量级。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询