标签 个人开发者 下的文章

https://www.v2ex.com/t/1188621 的后续

虽然证据很多,但这个作者死猪不怕开水烫。看了 reddit 才知道他是惯犯了,而且对版权问题很了解,可能有律师指点。作为个人开发者是没有精力和财力诉诸公堂的,除了道德谴责做不了什么。遇到这种人非常损伤开源热情。

背景

在开发“智能带办”应用时涉及到用户体系,开发阶段使用固定验证码形式跑通,在上线前准备接入短信服务时却遇到了难题,短信服务目前只对企业开发者开放了,个人开发者没办法再使用短信服务。为了顺利上架,退后求其次,改为了使用邮箱验证码等了。

邮箱验证码登录有两个弊端,一是不方便,很多用户进来发现是邮箱验证码登录不方便直接就退出应用了;二是合规风险,在申请安全评估报告时如果涉及到用户体系要求实名,邮箱没办法保证实名,还得再加入额外的实名体系,不仅麻烦而且很多都限制个人开发者没法使用。

其实最开始也考虑过要接入华为登录,看了一键登录文档发现也是只针对企业开发者,以为也是只有企业开发者可以使用,后面看了“华为账号登录”后发现个人开发者也可以使用,只是取不到手机号,正好不使用手机号可以规避合规方面的风险。
image.png

华为登录能力介绍

华为账号服务简介

Account Kit(华为账号服务)提供简单、快速、安全的登录功能,让用户快捷地使用华为账号登录应用。用户授权后,Account Kit可提供头像、昵称、手机号码等信息,帮助应用更了解用户。华为账号服务提供了登录、获取华为账号用户信息、未成年模式等。在开发过程中涉及下面几个概念:

  • OpenID:应用维度用户标识符,是华为账号用户在应用/元服务的唯一标识。不同应用/元服务(不管是否在同一个开发者账号下)获取到用户的OpenID不同。
  • UnionID:开发者维度用户标识符,华为账号用户同一开发者账号下的唯一标识。开发者有多个应用/元服务时,同一个开发者账号下的应用/元服务获取到用户的UnionID相同。
  • GroupUnionID:关联主体账号组维度用户标识符,是华为账号用户在关联主体账号组内的唯一标识。不同开发者账号加入同一关联主体账号组后,其组内所有开发者的应用/元服务获取到用户的GroupUnionID相同。
  • permission:数据或接口权限,通过该权限判断应用是否能获取对应数据或调用对应接口。
  • scopes:scope列表,用于获取用户数据。开发者向华为账号服务申请不同类型用户数据的标识。比如头像昵称(profile)、匿名手机号(quickLoginAnonymousPhone)等。
  • Authorization Code:授权码,用户使用华为账号登录成功之后,可通过返回的凭据解析出授权码,通过授权码可获取Access Token、Refresh Token、ID Token等。
  • Access Token:访问凭证,是访问被权限管控资源的应用级凭证。可使用Access Token调用获取用户信息接口获取用户信息。
  • ID Token:用户身份凭证,是OIDC (OpenID Connect) 协议相对于OAuth 2.0 协议扩展的一个用户身份凭证,包含用户信息。用户使用华为账号登录成功之后,可通过返回的凭据解析出Authorization Code、ID Token等数据。

在我们接口华为用户服务后,可以使用OpenId和UnionID绑定我们自己的账号体系。

华为账号服务交互流程

由于个人开发者无法使用“一键登录”,本文主要介绍 “华为账号登录”按钮登录。使用按钮登录我们可以使用Account Kit提供的华为账号登录按钮及服务端交互获取华为账号用户身份标识UnionID、OpenID,通过UnionID、OpenID完成用户登录;或者与应用账号完成绑定,绑定后用于登录或者验证。

华为账号登录按钮包含文本、标志和文本、标志三种样式,以满足应用对界面风格一致性和灵活性的要求。
image.png

账号服务开发者与华为能力交互流程如下图所示:
image.png

交互流程说明如下:
流程说明:

  1. 调用登录按钮展示登录页阶段(序号1-3):

    1. 用户打开应用进行登录,应用设置LoginType类型为LoginType.ID后拉起应用自己的登录页并展示“华为账号登录”按钮,用户点击按钮,请求华为账号授权信息。
  2. 用户点击登录阶段(序号4-6):

    1. 如华为账号未登录,将拉起华为账号登录页,用户登录后,将返回Authorization Code等数据给应用。
    2. 如华为账号已登录,将直接返回Authorization Code等数据给应用。
  3. 用户关联应用账号阶段(序号7-16):

    1. 应用服务端通过Authorization Code获取到Access Token,再使用Access Token调用解析凭证接口获取用户相关信息。通过Authorization Code凭证获取用户信息可以有效避免黑客通过数据遍历、身份伪造、重放攻击等手段导致的安全风险。
    2. 应用服务端将业务登录凭证SessionId、UnionID/OpenID传给应用,应用获取到UnionID/OpenID可用于判断华为账号是否登录等功能。
    3. 应用对用户身份标识UnionID/OpenID、业务登录凭证SessionId信息进行认证后,通过UnionID/OpenID判断用户是否已关联应用系统数据库,如已关联,则完成用户登录;如未关联,则创建新用户,绑定UnionID/OpenID。

华为账号服务提供了LoginWithHuaweiIDButton组件,构造中需要传入LoginWithHuaweiIDButtonParams类型和 LoginWithHuaweiIDButtonController类型的参数,LoginWithHuaweiIDButtonParams属性如下:

名称类型只读可选说明
styleStyleLoginWithHuaweiIDButton组件的样式。支持样式包括:BUTTON_RED、BUTTON_WHITE、BUTTON_WHITE_OUTLINE、BUTTON_BLACK、ICON_RED、ICON_WHITE、ICON_WHITE_OUTLINE、ICON_BLACK、ICON_GRAY、BUTTON_GRAY、BUTTON_CUSTOM。
borderRadiusnumber按钮边框圆角半径。取值范围:[0,+∞),值小于0时,按0处理。默认值:height属性取值的一半。单位:vp。
iconRadiusnumberIcon类型按钮的半径。取值范围:[0,+∞),值小于0时,按0处理。默认值:24。单位:vp。
supportDarkModeboolean表示按钮的样式是否随系统深浅色模式变化。true:按钮的样式会随着系统深浅色模式变化。false:按钮的样式不会随着系统深浅色模式变化。默认值:true。
loginTypeLoginType华为账号登录类型。默认值:LoginType.ID。一键登录请使用LoginType.QUICK_LOGIN。
textAndIconStyleboolean是否展示图文混合样式的华为账号登录按钮。true:按钮支持Icon和文字混合样式。false:按钮仅支持文本样式。默认值:false。当loginType不等于LoginType.QUICK_LOGIN且style等于BUTTON_RED、BUTTON_WHITE、BUTTON_WHITE_OUTLINE、BUTTON_BLACK、BUTTON_GRAY时该参数生效。起始版本:5.0.0(12)
customButtonParamsCustomButtonParamsBUTTON_CUSTOM按钮样式参数。起始版本:5.0.0(12)
verifyPhoneNumberboolean华为账号用户在过去90天内未进行短信验证,是否拉起Account Kit提供的短信验证码页面。true:拉起Account Kit提供的短信验证码页面。false:不拉起Account Kit提供的短信验证码页面。需要应用验证手机号时效性。默认值:true。起始版本:5.0.0(12)
extraStyleExtraStyle如果应用想使用华为账号提供的固定样式之外的效果,可使用此接口自定义按钮样式。起始版本:5.0.0(12)
loginButtonTextTypeLoginButtonTextType当loginType为LoginType.QUICK_LOGIN时,可传入此参数,控制按钮文本内容显示。默认值:LoginButtonTextType.QUICK_LOGIN。当该参数为LoginButtonTextType.QUICK_LOGIN时,按钮文本内容显示“华为账号一键登录”。当该参数为LoginButtonTextType.QUICK_REGISTRATION时,按钮文本内容显示“华为账号一键注册”。起始版本:5.0.0(12)
riskLevelboolean是否需要获取华为账号用户风险等级。仅登录类型为LoginType.QUICK_LOGIN时需要设置该参数。true:需要获取用户风险等级。false:不获取用户风险等级。默认值:false。起始版本:5.1.0(18)
securityVerificationboolean用户开启华为账号一键登录增强身份验证后,应用会在登录过程中通过华为账号使用生物识别或短信进行身份验证。如果需要获取用户一键登录增强身份验证的开关状态,需设置该字段为false。仅登录类型为LoginType.QUICK_LOGIN时需要设置该参数。true:响应结果HuaweiIDCredential将不会返回 enableSecurityVerification。false:响应结果HuaweiIDCredential将返回 enableSecurityVerification。默认值:true。起始版本:6.0.0(20)

智能带办接入过程

目前应用只支持华为登录,页面UI如下:
image.png

在页面中配置红色的LoginWithHuaweiIDButton:

LoginWithHuaweiIDButton({  
    params: {  
      // LoginWithHuaweiIDButton支持的样式  
      style: loginComponentManager.Style.BUTTON_RED,  
      // 账号登录按钮在登录过程中展示加载态  
      extraStyle: {  
        buttonStyle: new loginComponentManager.ButtonStyle().loadingStyle({  
          show: true  
        })  
      },  
      // LoginWithHuaweiIDButton的边框圆角半径  
      borderRadius: 24,  
      // LoginWithHuaweiIDButton支持的登录类型  
      loginType: loginComponentManager.LoginType.ID,  
      // LoginWithHuaweiIDButton支持按钮的样式跟随系统深浅色模式切换  
      supportDarkMode: true  
    },  
    controller: this.controller  
  })  
}  
.height(40)  
.width('100%')  
.margin({top:50})  
.padding({left:25, right:25})

控制器controller定义如下:

controller: loginComponentManager.LoginWithHuaweiIDButtonController =  
  new loginComponentManager.LoginWithHuaweiIDButtonController()  
    .setAgreementStatus(loginComponentManager.AgreementStatus.NOT_ACCEPTED)  
    .onClickLoginWithHuaweiIDButton((error: BusinessError, response: loginComponentManager.HuaweiIDCredential) => {  
      if (error) {  
        this.dealAllError(error);  
        return;  
      }  
  
      if (response) {  
        Logger.i(TAG, 'Succeeded in getting response.');  
        const authCode = response.authorizationCode;  
        // 开发者处理authCode  
        this.getUserInfoPermission(authCode)  
      }  
    });

在controller中获取回调,如果登录成功则通过authorizationCode继续申请用户华为头像和昵称授权:

getUserInfoPermission(authCode:string){  
  // 创建授权请求,并设置参数  
  const authRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest();  
  // 获取头像昵称需要传如下scope  
  authRequest.scopes = ['profile'];  
  // 若开发者需要进行服务端开发以获取头像昵称,则需传如下permission获取authorizationCode  
  authRequest.permissions = ['serviceauthcode'];  
  // 用户是否需要登录授权,该值为true且用户未登录或未授权时,会拉起用户登录或授权页面  
  authRequest.forceAuthorization = true;  
  // 用于防跨站点请求伪造  
  authRequest.state = util.generateRandomUUID();  
  // 执行授权请求  
  try {  
    const controller = new authentication.AuthenticationController(this.getUIContext().getHostContext());  
    controller.executeRequest(authRequest).then((data) => {  
      const authorizationWithHuaweiIDResponse = data as authentication.AuthorizationWithHuaweiIDResponse;  
      const state = authorizationWithHuaweiIDResponse.state;  
      if (state && authRequest.state !== state) {  
        Logger.i(TAG, `Failed to authorize. The state is different, response state: ${state}`);  
        return;  
      }  
      Logger.i(TAG,'Succeeded in authentication.');  
      const authorizationWithHuaweiIDCredential = authorizationWithHuaweiIDResponse?.data;  
      const avatarUri = authorizationWithHuaweiIDCredential?.avatarUri;  
      const nickName = authorizationWithHuaweiIDCredential?.nickName;  
      // 开发者处理avatarUri, nickName  
      const authorizationCode = authorizationWithHuaweiIDCredential?.authorizationCode;  
      Logger.i(TAG, 'getUserInfoPermission:' + JsonUtils.toJSONString(authorizationWithHuaweiIDCredential))  
      this.sendLoginRequest(authorizationCode??authCode)  
      // 涉及服务端开发以获取头像昵称场景,开发者处理authorizationCode  
    }).catch((err: BusinessError) => {  
      this.dealAllError(err);  
    });  
  } catch (error) {  
    this.dealAllError(error);  
  }  
}

用户授权成功后请求服务端接口,服务端通过authorizationCode调用华为服务获取accessToken,接着获取用户信息,绑定自己的账号体系返回自己账号体系的token即可。通过下面接口获取用户级凭证:

POST /oauth2/v3/token HTTP/1.1
Host: oauth-login.cloud.huawei.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=<code>&client_id=<client_id>&client_secret=<client_secret>

接着通过下面示例获取用户昵称和头像:

POST /rest.php?nsp_svc=GOpen.User.getInfo HTTP/1.1
Host: account.cloud.huawei.com
Content-Type: application/x-www-form-urlencoded

access_token=<Access Token>

必须在手机上调起授权获取用户授权后这里才可以请求到用户头像和昵称。

总结

本次“智能带办”应用的登录体系接入实践,源于上线前短信服务仅对企业开发者开放的限制,迫使我们从固定验证码、邮箱验证码转向华为账号登录方案。初期因误判“一键登录”仅限企业开发者而忽略“华为账号登录”,后发现个人开发者虽无法获取手机号,但恰好规避了邮箱登录的用户体验差(用户因不便退出)与实名合规风险(需额外实名体系),成为关键破局点。

华为账号服务(Account Kit)通过OpenID(应用唯一标识)、UnionID(开发者唯一标识)等核心概念,为个人开发者提供了安全高效的登录能力:既支持自定义样式的登录按钮(如本文配置的红色BUTTON_RED按钮),又通过Authorization CodeAccess Token→用户信息的流程保障安全,避免身份伪造等风险。接入过程中,我们通过LoginWithHuaweiIDButton组件实现前端交互,结合服务端解析凭证绑定自有账号体系,最终完成用户登录闭环。

此次实践的核心启示在于:面对企业级服务限制时,需深度挖掘平台对个人开发者的差异化能力——华为账号登录虽不提供手机号,却以“去实名化”特性解决了合规痛点,同时依托成熟的OAuth 2.0/OIDC协议与丰富组件(如支持深色模式、自定义圆角的按钮),兼顾了开发效率与用户体验。未来,可进一步探索UnionID在多应用间的用户打通能力,或结合GroupUnionID拓展关联主体场景,持续完善登录体系的灵活性与扩展性。

做出海工具站,绕不开的一道坎:收款

海外工具站最常见的支付方案是 Stripe ,
但现实是 Stripe 不支持大陆个人直接注册。

那么 Creem,是目前性价比非常高的一种选择。
Creem 的优势在于:

  • 不需要护照
  • 不需要营业执照
  • 只需要 身份证 + 支付宝
  • 支持用户使用 银行卡 / Google Pay 付款
  • 覆盖大多数海外用户的支付习惯
  • 非常适合个人出海、MVP 阶段

👉 等后期跑通变现、有稳定收入后,再考虑迁移到 Stripe,是一个非常合理的路径。

(原本是想放截图的,但是暂时没有找到插图的方式)

一、注册账号( 10 分钟搞定)

首次注册 Creem 后,需要先 创建一个 Store。这里非常简单:

  • Store Name:直接填写你的网站名即可
  • 不需要复杂描述,后面都可以修改

进入 Creem 后,默认是测试模式( Test Mode )。在测试模式下:所有支付都是 测试卡、不会真实扣钱、适合在本地或测试站调接口

👉 测试卡非常常见,随便找个 AI 问就能拿到。

二、完善账户提现和产品信息

Step 1:账户提现信息(生产环境)

⚠️ 一定要先切换到 生产环境( Production / Live )

切完后,完善提💰信息,Balance → Payout account → Create new account

接下来,按页面提示,逐步完善信息,信息填完后即可提交。

如果有不清楚的地方,可以直接找客服(左下角在线客服,回复速度还可以)。

Step 2:产品信息

1️⃣ 价格设置

这里只遵循一个原则:

你打算怎么卖,就怎么填。

看你想怎么卖你的产品,面向什么用户,单次付费还是订阅,价格区间,看用户能接受什么样的价位和档次。
只要构思好了,表单照填即可。

⚠️ 重要避坑提醒:

测试环境 & 生产环境的产品信息,都需要各填一遍。因为它们使用的是 不同的 API Key,如果只改测试环境,代码还没发版,生产环境数据却变了。用户会一脸懵,你也会一脸懵。

2️⃣ 网站必备页面(审核关键)

先给结论,Creem 审核非常看重以下内容:

Privacy Policy (隐私政策)Terms of Service (服务条款)联系邮箱(如:support@yourdomainfeedback@yourdomain)、检查是否存在违规内容(如:虚假评价、虚假销量,有就删掉)

我第一次提审为什么没过?

我一开始的想法是:

“Creem 应该只审核身份吧?
支付功能我还没完全接好,先提审看看。
等通过审核,再把支付功能接上去。”

于是:网站还没正式接支付、一些页面也没写、就直接提交了审核

一般 48h 内会有回复,结果我收到了一封邮件:未提供网站,信息不完整

后来我才认真看官方文档,发现他们有一整套
Account Review Checklist

如果支付功能还在测试,怎么办?

如果你的支付功能还没上生产环境,可以这样做:提交 正式网站(不带测试支付)、说明支付功能仍在测试阶段、提供测试地址(如 xxx.test)、同时提交产品截图(能看到价格、能看到产品形态 )

这样审核方也好判断:产品是否真实存在、是否可售、是否符合合规要求

⚠️避坑点

1️⃣一定不要出现任何虚假信息。
不管是虚假评价、虚假销量,还是一些看起来“无伤大雅”的营销描述,在 Creem 审核阶段都非常容易被卡。至少在审核期间,建议全部删除,否则基本就是直接不过。

2️⃣二次提审一定要做到“对症下药”。
Creem 的二次提审通常是通过邮件沟通,对方会非常明确地指出你当前不符合要求的地方,这时候不要只回复一句“我已经修改了”,而是要针对每一条反馈逐项解决,并附上对应的证据,比如截图、链接或页面地址,否则很容易再次被拒,甚至进入更长的等待周期。

3️⃣不要反复硬提审。
目前 Creem 并没有公开说明最多可以提审多少次,但不少伙伴的反馈是,提交次数一多,审核等待时间会明显被拉长。结合实际经验来看,只要前期准备到位,认真按照清单补齐内容,三次以内基本都能通过,没有必要赌运气。

我的整个提审周期

从第一次提交审核到最终通过,我前后大概花了 11 天,时间拉长主要是两个原因。

1️⃣我自己没有勤看邮箱。
手机收不到 Gmail 提醒,必须打开电脑、登录 Gmail 才能看到审核邮件,结果提审后一周才发现对方早就回复过我,指出我缺少网站链接。再一对照自查清单,才发现产品截图、价格展示等内容也都没准备齐,于是花了一天时间补齐静态页面,又用一天时间重新梳理产品定价,剩下的时间基本都在 vibe coding 。

2️⃣刚好赶上他们的圣诞节假期。
理论上 Creem 官方给的审核回复时间是 48 小时内,但叠加假期之后,实际大概等了 4 天左右才收到回复,好在后续流程就比较顺利了,再次回复邮件后就直接通过。

小小结

如果你现在正在做 AI 出海工具站,正好卡在「收款」这一步,又不想一开始就折腾海外公司、税务和一整套复杂结构,那么 Creem 这条路径是非常值得参考的。

先把 MVP 跑起来,先把现金流跑通,再去考虑更复杂、更长期的方案,往往是对个人开发者来说性价比最高的选择。