portaly-payment

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Portaly Vibe Payment Integration

Portaly Vibe 支付集成

Use this skill to help a human user finish a Portaly Vibe API integration quickly. Keep answers operational: prefer step lists, API request and response bullets, and copy-ready examples over long architecture explanations.
使用此技能帮助用户快速完成Portaly Vibe API集成。回答需注重实操性:优先使用步骤列表、API请求与响应要点、可直接复制的示例,而非冗长的架构说明。

Portaly Vibe Payment Environments

Portaly Vibe 支付环境

Portaly Vibe Payment supports two modes per API key: live and test.
Portaly Vibe Payment支持每个API密钥对应两种模式:live(生产)test(测试)

API Host

API主机地址

Use the following API host for both modes:
  • https://portaly.cc
两种模式均使用以下API主机地址:
  • https://portaly.cc

Payment site

支付站点

Payment site URLs to which buyers are redirected for checkout:
  • https://portaly.ai
买家结账时将被重定向至以下支付站点URL:
  • https://portaly.ai

Mode behavior

模式行为

AspectLive modeTest mode
API key prefix
pcs_live_
pcs_test_
Payment providerTapPay productionTapPay sandbox
Order storage
orders
collection
sandboxOrders
collection
Callback payload
mode: "live"
or absent
mode: "test"
  • Mode is set at API key creation time and cannot be changed after creation.
  • A single merchant (
    profileId
    ) can have both a live key and a test key active at the same time.
  • All API endpoints accept both live and test keys. The mode is derived from the key, not from a request parameter.
  • Test mode is intended for integration testing. Real charges are not made in test mode when using TapPay sandbox credentials.
  • Plans and merchant config are shared across modes. They belong to the
    profileId
    , not to the API key mode. A plan created with a live key is visible and usable with a test key, and vice versa. Do not create duplicate plans when switching between live and test keys — query existing plans first with
    GET /api/creator-subscription/plans?profileId={profileId}
    and reuse them.
维度生产模式测试模式
API密钥前缀
pcs_live_
pcs_test_
支付提供商TapPay生产环境TapPay沙箱环境
订单存储
orders
集合
sandboxOrders
集合
回调负载
mode: "live"
或缺失
mode: "test"
  • 模式在API密钥创建时设置,创建后无法更改。
  • 单个商家(
    profileId
    )可同时拥有有效的生产密钥和测试密钥。
  • 所有API端点均接受生产和测试密钥,模式由密钥决定,而非请求参数。
  • 测试模式用于集成测试,使用TapPay沙箱凭证时不会产生真实扣费。
  • 计划和商家配置在不同模式间共享。它们属于
    profileId
    ,而非API密钥模式。使用生产密钥创建的计划可通过测试密钥查看和使用,反之亦然。切换生产和测试密钥时请勿创建重复计划——先通过
    GET /api/creator-subscription/plans?profileId={profileId}
    查询现有计划并复用。

Quick Start

快速开始

  • Before starting, AI agent should ask the human user to claim or create a Portaly Vibe Payment API key/CallbackSecret in the Portaly Vibe Dashboard at
    https://portaly.ai/dashboard
    and store the issued secret material safely.
  • Ask the human user whether they want a live or test key. Recommend starting with a test key for integration development.
  1. Confirm what the human user is trying to build. Prepare for payment integration tasks such as:
    1. create merchant config
    2. create subscription plans
    3. upload merchant or plan images (Agent should ask human user to provide image assets if needed)
  2. After setup, integrate the checkout session creation and callback handling into current system:
    1. create checkout session before buyer initiates payment
    2. redirect buyer to Portaly vibe checkout
    3. verify and consume the callback from Portaly after checkout completion
    4. if the integration needs subscription lifecycle management, also wire cancel and resume APIs for recurring plans
    5. if the integration needs subscriber self-service (letting subscribers manage their own subscriptions), wire the portal session API
  3. Start with
    references/api-contract.md
    . Use it for endpoint lists, auth, request bodies, response bodies, and callback headers.
  4. Load
    references/checkout-and-renewal.md
    only when needed. Use it only as supplemental reference when the human user asks about post-checkout charging, renewal, payout, invoice, or bridge-order behavior.
  5. Return implementation-ready output. Prefer numbered steps, API endpoint lists, request and response bullets, and Node.js or TypeScript examples.
  • 开始前,AI Agent应要求用户在Portaly Vibe控制台(
    https://portaly.ai/dashboard
    )申领或创建Portaly Vibe Payment API密钥/CallbackSecret,并妥善存储生成的保密信息。
  • 询问用户需要生产密钥还是测试密钥。建议从测试密钥开始进行集成开发。
  1. 确认用户的开发目标。 准备好以下支付集成任务:
    1. 创建商家配置
    2. 创建订阅计划
    3. 上传商家或计划图片(若需要,Agent应要求用户提供图片资源)
  2. 完成设置后,将结账会话创建和回调处理集成到现有系统中:
    1. 在买家发起支付前创建结账会话
    2. 将买家重定向至Portaly Vibe结账页面
    3. 结账完成后,验证并处理Portaly发送的回调
    4. 若集成需要订阅生命周期管理,还需对接周期性计划的取消和恢复API
    5. 若集成需要订阅者自助服务(让订阅者自行管理订阅),对接门户会话API
  3. references/api-contract.md
    开始。 使用该文档获取端点列表、认证方式、请求体、响应体和回调头信息。
  4. 仅在需要时加载
    references/checkout-and-renewal.md
    。 仅当用户询问结账后的扣费、续费、付款、发票或桥接订单行为时,将其作为补充参考。
  5. 返回可直接用于实现的输出。 优先使用编号步骤、API端点列表、请求与响应要点,以及Node.js或TypeScript示例。

Output Style

输出风格

  • Write for an AI agent that is helping a human user complete integration work.
  • Lead with the next concrete steps the human should take.
  • Be explicit when an API can be called directly by the Agent with the Portaly Vibe Payment API key.
  • Prefer using the setup APIs directly for merchant config, plan creation, plan updates, image uploads, and checkout session creation when the user has already provided valid credentials and required inputs.
  • Use lists for:
    • setup steps
    • API endpoints
    • required headers
    • request fields
    • response fields
    • callback verification steps
  • Prefer concise code samples in JavaScript or TypeScript when the user does not ask for another stack.
  • Keep Portaly-owned behavior and third-party-owned behavior clearly separated.
  • 为帮助用户完成集成工作的AI Agent撰写内容。
  • 首先列出用户下一步需执行的具体步骤。
  • 明确说明哪些API可由Agent使用Portaly Vibe Payment API密钥直接调用。
  • 当用户已提供有效凭证和必要输入时,优先直接调用设置API完成商家配置、计划创建、计划更新、图片上传和结账会话创建。
  • 使用列表展示:
    • 设置步骤
    • API端点
    • 必填请求头
    • 请求字段
    • 响应字段
    • 回调验证步骤
  • 若用户未指定技术栈,优先提供JavaScript或TypeScript的简洁代码示例。
  • 清晰区分Portaly自有行为和第三方自有行为。

Workflow

工作流程

1. Apply for the API key

1. 申请API密钥

  • Require a Portaly Vibe Payment API key and CallbackSecret for this integration.
  • Instruct the human user to apply for or create the Portaly Vibe Payment API key in the Portaly Vibe Dashboard at
    https://portaly.ai/dashboard
    .
  • Ask whether the user wants a live key (
    pcs_live_…
    ) or a test key (
    pcs_test_…
    ). Recommend starting with a test key for development and switching to live for production.
  • Be explicit that this step is performed by a human operator in Portaly Vibe Dashboard, not by the third-party integration code.
  • Tell the human user to store the issued secret material safely, or store it on the user's behalf only in an appropriate secret manager or secure environment store.
  • Explain that the API key is used for bearer authentication in API calls and the
    callbackSecret
    is used for verifying the authenticity of callbacks from Portaly If user asking.
  • Never ask the user to paste the API key or
    callbackSecret
    into chat.
    Chat transcripts can be logged, cached, or echoed back by the model in summaries, diffs, or tool call arguments. Treat secrets as values the agent never needs to see in plaintext.
  • Instead, instruct the human user to place the secrets into
    .env
    themselves (via their editor or shell), using this template:
    PORTALY_API_KEY=pcs_live_xxx        # or pcs_test_xxx for test mode
    PORTALY_CALLBACK_SECRET=xxx
  • The agent reads these at runtime via
    process.env.PORTALY_API_KEY
    (Node) or
    os.environ["PORTALY_API_KEY"]
    (Python) — it never needs the literal secret value in-context.
  • If the project uses a secret manager (1Password CLI, Doppler, AWS/GCP Secrets Manager, Vault, etc.), prefer that over
    .env
    .
  • Before proceeding, verify that
    .gitignore
    includes
    .env
    .
    If
    .gitignore
    does not exist or does not include
    .env
    , create or update it immediately. Never allow credentials to be committed to version control.
  • If the user does paste a secret into chat by mistake, advise them to rotate the key in the Portaly Vibe Dashboard before using it — assume the pasted value is compromised.
  • 此集成需要Portaly Vibe Payment API密钥和CallbackSecret。
  • 指导用户在Portaly Vibe控制台(
    https://portaly.ai/dashboard
    )申请或创建Portaly Vibe Payment API密钥。
  • 询问用户需要生产密钥(
    pcs_live_…
    )还是测试密钥(
    pcs_test_…
    )。建议开发阶段使用测试密钥,生产阶段切换为生产密钥。
  • 明确说明此步骤需由人工在Portaly Vibe控制台完成,而非第三方集成代码。
  • 告知用户妥善存储生成的保密信息,或仅在合适的密钥管理器或安全环境存储中代表用户存储。
  • 解释API密钥用于API调用中的Bearer认证,
    callbackSecret
    用于验证Portaly回调的真实性(若用户询问)。
  • 切勿要求用户在聊天中粘贴API密钥或
    callbackSecret
    。聊天记录可能被记录、缓存或在模型的摘要、差异或工具调用参数中回显。将密钥视为Agent永远无需以明文形式查看的值。
  • 取而代之,指导用户自行(通过编辑器或Shell)将密钥放入
    .env
    文件,使用以下模板:
    PORTALY_API_KEY=pcs_live_xxx        # 测试模式使用pcs_test_xxx
    PORTALY_CALLBACK_SECRET=xxx
  • Agent在运行时通过
    process.env.PORTALY_API_KEY
    (Node.js)或
    os.environ["PORTALY_API_KEY"]
    (Python)读取这些值——永远无需在上下文环境中使用密钥的字面量。
  • 若项目使用密钥管理器(1Password CLI、Doppler、AWS/GCP Secrets Manager、Vault等),优先使用密钥管理器而非
    .env
    文件。
  • 继续操作前,确认
    .gitignore
    已包含
    .env
    。若
    .gitignore
    不存在或未包含
    .env
    ,立即创建或更新。绝不能允许凭证被提交到版本控制系统。
  • 若用户不慎在聊天中粘贴了密钥,建议他们在使用前立即在Portaly Vibe控制台轮换密钥——假设粘贴的值已泄露。

2. Configure merchant settings

2. 配置商家设置

  • Agent should perform these setup actions directly by API call with the Portaly Vibe Payment API key.
  • Use the Config APIs when the human user needs to set merchant branding before any product goes live.
  • AI Agent should ask the human user to provide a
    merchantLogo
    image asset, use the config image upload API to upload image to Portaly. The merchant logo is optional — if the user does not have one ready, skip this step and proceed with plan creation.
  • Use
    PUT /api/creator-subscription/config
    and
    POST /api/creator-subscription/config/images
    to set up merchant branding with the Portaly Vibe Payment API key.
  • Agent应使用Portaly Vibe Payment API密钥直接调用API完成这些设置操作。
  • 当用户需要在产品上线前设置商家品牌时,使用配置API。
  • AI Agent应要求用户提供
    merchantLogo
    图片资源,使用配置图片上传API将图片上传至Portaly。商家Logo为可选项——若用户未准备好,可跳过此步骤直接创建计划。
  • 使用
    PUT /api/creator-subscription/config
    POST /api/creator-subscription/config/images
    ,通过Portaly Vibe Payment API密钥设置商家品牌。

3. Create a valid subscription plan

3. 创建有效的订阅计划

  • Agent should perform plan creation, plan updates, and plan image uploads directly by API call with the Portaly Vibe Payment API key.
  • Before creating a new plan, always query existing plans with
    GET /api/creator-subscription/plans?profileId={profileId}
    using the current API key. Plans are shared across live and test modes — if a suitable plan already exists, reuse it instead of creating a duplicate.
  • Require at least one active plan in Portaly before creating a checkout session.
  • Use the Plan APIs to create or update the product basics that the human user wants to list on Portaly.
  • Confirm the plan name, description, amount, currency, billing period (
    monthly
    ,
    yearly
    , or
    one-time
    ), pricing type (
    fixed
    or
    dynamic
    ), and status match the intended product.
  • For dynamic pricing plans: set
    pricingType
    to
    dynamic
    and
    billingPeriod
    to
    one-time
    . The amount is not set on the plan; instead, the caller passes
    amount
    when creating each checkout session.
  • If the third party has its own product catalog, persist the Portaly
    planId
    together with the merchant's internal product or entitlement identifier.
  • AI Agent should ask the human user to provide a plan image, use the plan image upload API to upload the image to Portaly.
  • Treat the
    checkoutUrl
    returned by Portaly as authoritative. Do not reconstruct it from guessed domains.
  • After creating or updating a plan, check the response
    name
    and
    description
    for garbled text (mojibake). If corrupted, fix shell encoding and use
    PUT /api/creator-subscription/plans/{planId}
    to correct it. See the Windows encoding note in Guardrails.
  • Agent应使用Portaly Vibe Payment API密钥直接调用API完成计划创建、计划更新和计划图片上传。
  • 创建新计划前,务必先查询现有计划:使用当前API密钥调用
    GET /api/creator-subscription/plans?profileId={profileId}
    。计划在生产和测试模式间共享——若已有合适的计划,应复用而非创建重复计划。
  • 创建结账会话前,Portaly中至少需有一个激活的计划。
  • 使用计划API创建或更新用户希望在Portaly上架的产品基础信息。
  • 确认计划名称、描述、金额、货币、计费周期(
    monthly
    yearly
    one-time
    )、定价类型(
    fixed
    dynamic
    )和状态与预期产品一致。
  • 对于动态定价计划:将
    pricingType
    设置为
    dynamic
    billingPeriod
    设置为
    one-time
    。计划中不设置金额;调用方创建每个结账会话时传入
    amount
  • 若第三方拥有自己的产品目录,需将Portaly的
    planId
    与商家内部的产品或权限标识符一起持久化存储。
  • AI Agent应要求用户提供计划图片,使用计划图片上传API将图片上传至Portaly。
  • 将Portaly返回的
    checkoutUrl
    视为权威地址,请勿通过猜测域名重新构造。
  • 创建或更新计划后,检查响应中的
    name
    description
    是否存在乱码。若出现乱码,修复Shell编码后使用
    PUT /api/creator-subscription/plans/{planId}
    更正。参考Guardrails中的Windows编码说明。

3.5 Create discount codes (optional)

3.5 创建折扣码(可选)

  • Use the Discount Code APIs after at least one plan exists.
  • A code carries an array of rules; each rule can target a different set of plans with its own discount and duration. Example: code
    EARLY2026
    with two rules — 50% off for 3 cycles (= 3 months) on the monthly plan, and 20% off for 1 cycle (= 1 year) on the yearly plan.
  • Per rule, confirm with the human user:
    • Discount type:
      fixed
      (TWD off) /
      percent
      (% off) /
      free
      (100% off).
    • Duration:
      repeating N cycles
      (default 1) or
      forever
      (typically with
      fixed
      ). One cycle equals one billing period — a month for a monthly plan, a year for a yearly plan.
    • appliesTo:
      all
      (fallback for any plan not covered by a specific rule) or
      specific
      planIds (e.g. yearly plan only). At most one
      all
      rule per code; planIds may not appear in more than one rule.
  • Code-level params:
    • Custom code: 3-40 chars,
      [A-Z0-9_-]
      . Stored and displayed in UPPERCASE; lookup is case-insensitive on input. Unique per profile. Immutable post-create.
    • Redemption window:
      redeemFrom
      /
      redeemBy
      .
    • Caps:
      maxRedemptions
      (total) /
      maxRedemptionsPerCustomer
      (per email).
  • Codes are shared across live and test modes (same as plans).
  • Codes also serve as ref codes — see the
    portaly-user
    skill for how to record
    signupRefCode
    at user registration. When a buyer with a recorded
    signupRefCode
    later checks out and verifies their email, Portaly auto-applies the matching rule, provided the code is still within its
    redeemBy
    window.
  • See
    references/discount-code-examples.md
    for example prompts and the parameter cheatsheet.
  • Money-moving guard: live-mode discount creation requires explicit user confirmation (same rule as live-mode plan creation).
  • 至少存在一个计划后,方可使用折扣码API。
  • 一个折扣码包含一组规则;每条规则可针对不同计划设置不同折扣和时长。示例:折扣码
    EARLY2026
    包含两条规则——月度计划3个周期(即3个月)打5折,年度计划1个周期(即1年)打8折。
  • 针对每条规则,与用户确认:
    • 折扣类型
      fixed
      (新台币减免)/
      percent
      (百分比折扣)/
      free
      (全额减免)。
    • 时长
      repeating N cycles
      (默认1个周期)或
      forever
      (通常搭配
      fixed
      类型)。一个周期等于一个计费周期——月度计划为一个月,年度计划为一年。
    • 适用范围
      all
      (未被特定规则覆盖的所有计划的 fallback)或
      specific
      指定
      planIds
      (例如仅年度计划)。每个折扣码最多包含一条
      all
      规则;
      planIds
      不得出现在多条规则中。
  • 折扣码级别的参数:
    • 自定义码:3-40个字符,仅允许
      [A-Z0-9_-]
      。存储和显示为大写;输入时不区分大小写。每个
      profileId
      下唯一。创建后不可修改。
    • 兑换窗口
      redeemFrom
      /
      redeemBy
    • 限制
      maxRedemptions
      (总兑换次数)/
      maxRedemptionsPerCustomer
      (每个邮箱的兑换次数)。
  • 折扣码在生产和测试模式间共享(与计划相同)。
  • 折扣码同时可作为推荐码——参考
    portaly-user
    技能了解如何在用户注册时记录
    signupRefCode
    。当记录过
    signupRefCode
    的买家后续结账并验证邮箱后,若折扣码仍在
    redeemBy
    窗口内,Portaly将自动应用匹配的规则。
  • 参考
    references/discount-code-examples.md
    获取示例提示和参数速查表。
  • 资金操作防护:生产模式下创建折扣码需用户明确确认(与生产模式下创建/更新计划的规则相同)。

4. Create the checkout session

4. 创建结账会话

  • Create a checkout session before the buyer initiates payment.
  • Call
    POST /api/creator-subscription/checkout-sessions
    with
    Authorization: Bearer {api_key}
    .
  • Send
    planId
    and optional
    successRedirectUrl
    ,
    cancelRedirectUrl
    ,
    callbackUrl
    ,
    merchantOrderNumber
    , and string-keyed
    metadata
    .
  • Optional
    discountCode
    : when provided, Portaly validates and applies the discount up-front. Invalid codes return
    400 INVALID_DISCOUNT_CODE
    . When omitted, Portaly attempts to auto-apply a discount via the buyer's
    signupRefCode
    after their email is verified inside hosted checkout (no extra call needed from the merchant).
  • Persist
    sessionId
    ,
    checkoutToken
    ,
    checkoutUrl
    , and
    expiresAt
    on the third-party side.
  • The session response includes
    appliedDiscount
    when a discount was applied at session creation;
    session.amount
    is always the post-discount amount the buyer will be charged.
  • Redirect the buyer to
    checkoutUrl
    .
  • 在买家发起支付前创建结账会话。
  • 使用
    Authorization: Bearer {api_key}
    调用
    POST /api/creator-subscription/checkout-sessions
  • 传入
    planId
    以及可选的
    successRedirectUrl
    cancelRedirectUrl
    callbackUrl
    merchantOrderNumber
    和字符串键的
    metadata
  • 可选参数
    discountCode
    :若提供,Portaly将提前验证并应用折扣。无效码将返回
    400 INVALID_DISCOUNT_CODE
    。若省略,Portaly将在买家在托管结账流程中验证邮箱后,尝试通过买家的
    signupRefCode
    自动应用折扣(无需商家额外调用)。
  • 在第三方系统中持久化存储
    sessionId
    checkoutToken
    checkoutUrl
    expiresAt
  • 会话响应中包含
    appliedDiscount
    表示创建会话时已应用折扣;
    session.amount
    始终为买家需支付的折扣后金额。
  • 将买家重定向至
    checkoutUrl

5. Let Portaly run hosted checkout

5. 由Portaly运行托管结账流程

  • Treat Portaly hosted checkout as a black box from the third-party perspective.
  • Do not ask the third party to collect card tokens or implement Portaly-owned payment steps.
  • 从第三方视角,将Portaly托管结账流程视为黑盒。
  • 切勿要求第三方收集卡片令牌或实现Portaly自有支付步骤。

6. Consume the result

6. 处理结果

  • The primary external confirmation is the signed callback to
    callbackUrl
    .
  • Callback is only dispatched when checkout status is
    completed
    .
    Non-completed outcomes (failed, canceled, expired) do not trigger a callback.
  • For non-completed outcomes, poll
    GET /api/creator-subscription/checkout-sessions/{sessionId}
    as a fallback.
  • Use manual
    POST /api/creator-subscription/checkout-sessions/{sessionId}/complete
    only as an exception flow when the user is building a non-hosted or recovery flow.
  • Current implementation contract:
    subscriptionId === checkoutSessionId === sessionId
    .
  • When a recurring checkout succeeds, human user's system may use the callback's
    sessionId
    directly as the
    subscriptionId
    for later cancel or resume API calls.
  • Make it explicit to the human user that this is the current Portaly implementation contract and should be persisted on their side after checkout completion.
  • 主要的外部确认是发送至
    callbackUrl
    的签名回调。
  • 仅当结账状态为
    completed
    时才会触发回调
    。未完成的结果(失败、取消、过期)不会触发回调。
  • 对于未完成的结果,可通过轮询
    GET /api/creator-subscription/checkout-sessions/{sessionId}
    作为备选方案。
  • 仅在用户构建非托管或恢复流程的异常场景下,才使用手动调用
    POST /api/creator-subscription/checkout-sessions/{sessionId}/complete
  • 当前实现约定
    subscriptionId === checkoutSessionId === sessionId
  • 当周期性结账成功时,用户系统可直接使用回调中的
    sessionId
    作为后续取消或恢复API调用的
    subscriptionId
  • 明确告知用户这是Portaly当前的实现约定,结账完成后需在其系统中持久化存储。

7. Verify and persist

7. 验证与持久化

  • Verify
    x-portaly-signature
    with the API key's
    callbackSecret
    .
  • Use the exact timestamp from
    x-portaly-timestamp
    .
  • Reject callbacks where
    x-portaly-timestamp
    is older than 5 minutes
    to prevent replay attacks. Note:
    x-portaly-timestamp
    is an ISO datetime string, not Unix seconds.
  • Serialize the callback payload with stable key ordering before HMAC.
  • Reference implementations live in
    scripts/sign_callback.py
    and
    scripts/sign_callback.mjs
    .
  • After verification, persist
    sessionId
    ,
    subscriptionId
    if present,
    merchantOrderNumber
    ,
    paymentReference
    ,
    paymentMethod
    ,
    status
    , and the raw callback body for auditing.
  • If the callback payload does not include
    subscriptionId
    , persist
    sessionId
    as the recurring subscription identifier because the current implementation uses
    sessionId
    as
    subscriptionId
    .
  • Use
    sessionId
    as an idempotency key
    — if a callback with the same
    sessionId
    has already been processed, skip duplicate handling to avoid double fulfillment.
  • callbackUrl
    must use HTTPS.
    Serving over plain HTTP exposes the
    callbackSecret
    signature and payload in transit.
  • Heads up — Portaly may auto-send a welcome/upgrade email when the callback fires. A successful (
    status: completed
    ) callback triggers Portaly's
    welcome_paid
    template by default. Symmetrically, a cancel call triggers
    subscription_canceled
    . If the vibe coder already sends their own purchase-confirmation or cancellation email, disable the matching template before going live with
    PUT /api/creator-email/templates/welcome_paid
    (or
    subscription_canceled
    ) carrying
    { "enabled": false }
    . See the
    portaly-email
    skill for the full list of email types and disable workflow.
  • 使用API密钥对应的
    callbackSecret
    验证
    x-portaly-signature
  • 使用
    x-portaly-timestamp
    中的精确时间戳。
  • 拒绝
    x-portaly-timestamp
    超过5分钟的回调
    以防止重放攻击。注意:
    x-portaly-timestamp
    是ISO日期时间字符串,而非Unix时间戳。
  • 在HMAC计算前,使用稳定的键顺序序列化回调负载。
  • 参考实现位于
    scripts/sign_callback.py
    scripts/sign_callback.mjs
  • 验证通过后,持久化存储
    sessionId
    (若存在则包含
    subscriptionId
    )、
    merchantOrderNumber
    paymentReference
    paymentMethod
    status
    以及原始回调体用于审计。
  • 若回调负载中未包含
    subscriptionId
    ,则将
    sessionId
    作为周期性订阅标识符持久化存储,因为当前实现使用
    sessionId
    作为
    subscriptionId
  • 使用
    sessionId
    作为幂等键
    ——若已处理过相同
    sessionId
    的回调,跳过重复处理以避免重复执行操作。
  • callbackUrl
    必须使用HTTPS
    。使用HTTP会导致
    callbackSecret
    签名和负载在传输过程中暴露。
  • 注意——回调触发时Portaly可能自动发送欢迎/升级邮件。默认情况下,成功(
    status: completed
    )的回调会触发Portaly的
    welcome_paid
    模板。同理,取消调用会触发
    subscription_canceled
    模板。若商家已自行发送购买确认或取消邮件,上线前需禁用对应模板:调用
    PUT /api/creator-email/templates/welcome_paid
    (或
    subscription_canceled
    )并传入
    { "enabled": false }
    。参考
    portaly-email
    技能获取完整的邮件类型列表和禁用流程。

8. Manage recurring subscriptions

8. 管理周期性订阅

  • Only recurring plans with
    billingPeriod = monthly | yearly
    support cancel or resume.
  • Cancellation means stopping the next recurring charge. It is not a refund. In your system, the rights or content associated should remain active until the end of the current paid period, which is indicated by
    cancelEffectiveAt
    in the subscription record.
  • Portaly currently supports merchant-system initiated subscription lifecycle actions through API key authenticated endpoints.
  • Use the same Portaly Vibe Payment API key for these calls.
Recurring management APIs:
  • GET /api/creator-subscription/subscriptions
    — list all subscriptions with pagination and filtering
  • GET /api/creator-subscription/subscriptions/{subscriptionId}
  • POST /api/creator-subscription/subscriptions/{subscriptionId}/cancel
  • POST /api/creator-subscription/subscriptions/{subscriptionId}/resume
Order query API:
  • GET /api/creator-subscription/orders
    — list payment/order records with pagination
Recurring management rules:
  • These APIs only accept
    Authorization: Bearer {api_key}
  • Do not use Firebase auth for merchant-system integrations
  • billingPeriod = one-time
    does not support cancel or resume
  • cancel
    marks the subscription as
    cancelAtPeriodEnd = true
  • resume
    only works before the subscription has become fully
    canceled
Cancel request body:
json
{
  "reason": "customer_requested",
  "reasonNote": "optional note"
}
Resume request body:
json
{}
What to persist for recurring lifecycle:
  • subscriptionId
  • sessionId
  • planId
  • billingPeriod
  • status
  • cancelAtPeriodEnd
  • cancelEffectiveAt
  • billingPeriod = monthly | yearly
    的周期性计划支持取消或恢复操作。
  • 取消意味着停止下一次周期性扣费,而非退款。在你的系统中,相关权限或内容应保持可用至当前付费周期结束,订阅记录中的
    cancelEffectiveAt
    会标明该时间。
  • Portaly目前支持商家系统通过API密钥认证的端点发起订阅生命周期操作。
  • 使用相同的Portaly Vibe Payment API密钥进行这些调用。
周期性管理API:
  • GET /api/creator-subscription/subscriptions
    —— 分页并过滤列出所有订阅
  • GET /api/creator-subscription/subscriptions/{subscriptionId}
  • POST /api/creator-subscription/subscriptions/{subscriptionId}/cancel
  • POST /api/creator-subscription/subscriptions/{subscriptionId}/resume
订单查询API:
  • GET /api/creator-subscription/orders
    —— 分页列出支付/订单记录
周期性管理规则:
  • 这些API仅接受
    Authorization: Bearer {api_key}
  • 商家系统集成请勿使用Firebase认证
  • billingPeriod = one-time
    不支持取消或恢复
  • cancel
    会将订阅标记为
    cancelAtPeriodEnd = true
  • resume
    仅在订阅完全变为
    canceled
    状态前有效
取消请求体:
json
{
  "reason": "customer_requested",
  "reasonNote": "optional note"
}
恢复请求体:
json
{}
周期性生命周期需持久化存储的信息:
  • subscriptionId
  • sessionId
  • planId
  • billingPeriod
  • status
  • cancelAtPeriodEnd
  • cancelEffectiveAt

8.5. Wire invitation email CTA (optional)

8.5 对接邀请邮件CTA(可选)

If the merchant plans to use Portaly's invitation-email flow to recruit followers (waitlist signups, campaigns), the CTA in those emails redirects through
https://portaly.ai/r/{code}
to a waitlist landing page. By default the page is hosted by Portaly; the vibe coder can also host it themselves on their own domain by setting
appBaseUrl
on the merchant config.
This is a separate concern from payment integration — for the full setup, install and follow the
portaly-email
skill:
npx skills add portaly-ai/portaly-skills --skill portaly-email
.
若商家计划使用Portaly的邀请邮件流程招募追随者(等待名单注册、营销活动),邮件中的CTA将通过
https://portaly.ai/r/{code}
重定向至等待名单落地页。默认情况下该页面由Portaly托管;商家也可通过在商家配置中设置
appBaseUrl
,在自有域名上托管该页面。
这与支付集成是独立的需求——完整设置请安装并遵循
portaly-email
技能:
npx skills add portaly-ai/portaly-skills --skill portaly-email

9. Enable subscriber self-service portal (optional)

9. 启用订阅者自助服务门户(可选)

  • Use this when the merchant wants subscribers to manage their own subscriptions directly.
  • The merchant backend creates a portal session via
    POST /api/creator-subscription/portal-sessions
    on
    https://portaly.ai
    , then redirects the subscriber to the returned
    portalUrl
    .
  • This is a server-to-server call — the API key must never be exposed to the client.
  • The subscriber lands on Portaly's hosted portal page, already authenticated via the session token. No additional login is required.
  • In the portal, subscribers can view subscriptions, cancel, resume, and view payment history.
  • Portal sessions expire after 30 minutes.
  • The merchant must provide a
    returnUrl
    so the subscriber can navigate back after managing their subscriptions.
  • See
    Portal Session (Subscriber Self-Service)
    in
    references/api-contract.md
    for full endpoint details and code examples.
  • 当商家希望订阅者自行管理订阅时使用此功能。
  • 商家后端通过在
    https://portaly.ai
    调用
    POST /api/creator-subscription/portal-sessions
    创建门户会话,然后将订阅者重定向至返回的
    portalUrl
  • 这是服务器到服务器的调用——API密钥绝不能暴露给客户端。
  • 订阅者将进入Portaly托管的门户页面,并通过会话令牌自动认证,无需额外登录。
  • 在门户中,订阅者可查看订阅、取消订阅、恢复订阅以及查看支付历史。
  • 门户会话30分钟后过期。
  • 商家必须提供
    returnUrl
    ,以便订阅者管理完订阅后返回原系统。
  • 参考
    references/api-contract.md
    中的「Portal Session (Subscriber Self-Service)」获取完整的端点详情和代码示例。

Preferred Response Shape

推荐响应结构

When answering with this skill, prefer this order:
  1. Goal summary
  2. Human setup steps
  3. API list
  4. Request fields
  5. Response fields
  6. Callback handling steps
  7. Example code
  8. Troubleshooting notes
使用此技能回答时,优先遵循以下顺序:
  1. 目标总结
  2. 用户需执行的设置步骤
  3. API列表
  4. 请求字段
  5. 响应字段
  6. 回调处理步骤
  7. 示例代码
  8. 故障排除说明

Guardrails

防护规则

  • Default to test mode for development. If the loaded key starts with
    pcs_live_
    , confirm with the human user that live mode is intended before making any API call. Never silently run against production billing.
  • Money-moving actions require explicit user confirmation. Before calling any of the following, state the exact action, target (
    subscriptionId
    /
    sessionId
    ), and mode (live/test), then wait for the user's "yes":
    • POST /subscriptions/{id}/cancel
    • POST /subscriptions/{id}/resume
    • POST /checkout-sessions/{id}/complete
      (manual completion)
    • Any plan creation/update in live mode
  • Do not batch or loop these actions across multiple subscriptions without per-action confirmation.
  • Prefer the hosted checkout flow whenever possible. It already handles email verification, payment-method persistence, callback dispatch, subscription creation, payment creation, invoice task creation, and order bridge writes.
  • Distinguish clearly between:
    • setup APIs that the Agent can call directly with the Portaly Vibe Payment API key
  • Do not invent provider behavior. TapPay and 91APP differ materially.
  • Do not assume callback delivery means success without checking the
    status
    and verified signature.
  • Do not derive subscription state from redirect success pages alone. Redirects are UX only; callback or status query is the source of truth.
  • Treat
    references/checkout-and-renewal.md
    as non-API background material. Load it only if the task explicitly touches recurring billing, payout, invoice follow-up, or bridge-order behavior.
  • Windows encoding: On Windows, run
    chcp 65001
    (cmd) or
    $OutputEncoding = [System.Text.Encoding]::UTF8
    (PowerShell) before API calls containing non-ASCII text. If a plan's
    name
    or
    description
    comes back garbled, fix encoding and
    PUT
    the correct values.
  • Rate limiting: All creator-subscription API endpoints (except
    POST /checkout-sessions
    ) are rate limited. Read endpoints allow 120 requests/min, write endpoints allow 20 requests/min. If a
    429
    response is received, use the
    Retry-After
    header to schedule retries. When paginating through large result sets, be mindful of the rate limit budget.
  • 开发阶段默认使用测试模式。若加载的密钥以
    pcs_live_
    开头,调用任何API前需与用户确认是否确实要使用生产模式。绝不能静默地在生产计费环境中运行操作。
  • 资金操作需用户明确确认。调用以下操作前,需说明具体操作、目标(
    subscriptionId
    /
    sessionId
    )和模式(生产/测试),然后等待用户回复「是」:
    • POST /subscriptions/{id}/cancel
    • POST /subscriptions/{id}/resume
    • POST /checkout-sessions/{id}/complete
      (手动完成)
    • 生产模式下的任何计划创建/更新
  • 未经逐个操作确认,请勿批量或循环对多个订阅执行这些操作。
  • 优先使用托管结账流程。该流程已处理邮箱验证、支付方式持久化、回调分发、订阅创建、支付创建、发票任务创建和桥接订单写入。
  • 清晰区分:
    • Agent可使用Portaly Vibe Payment API密钥直接调用的设置API
  • 请勿虚构支付提供商行为。TapPay和91APP的行为存在显著差异。
  • 请勿仅通过回调送达就判定操作成功,需同时检查
    status
    和已验证的签名。
  • 请勿仅通过重定向成功页面推导订阅状态。重定向仅用于用户体验;回调或状态查询才是可信来源。
  • references/checkout-and-renewal.md
    视为非API背景资料。仅当任务明确涉及周期性计费、付款、发票跟进或桥接订单行为时才加载。
  • Windows编码:在Windows系统中,调用包含非ASCII文本的API前,需运行
    chcp 65001
    (cmd)或
    $OutputEncoding = [System.Text.Encoding]::UTF8
    (PowerShell)。若计划的
    name
    description
    返回乱码,修复编码后使用
    PUT
    请求更正。
  • 速率限制:所有创作者订阅API端点(
    POST /checkout-sessions
    除外)均有速率限制。读取端点允许120次请求/分钟,写入端点允许20次请求/分钟。若收到
    429
    响应,使用
    Retry-After
    头安排重试。分页查询大量结果时,需注意速率限制配额。

Deliverables

交付成果

When using this skill, aim to return one or more of:
  • a minimal step-by-step integration plan for the human user
  • a flat list of relevant APIs
  • request and response field breakdowns
  • callback verification code in the user's stack
  • sample
    curl
    ,
    fetch
    , or TypeScript snippets
  • a troubleshooting list keyed by session status
使用此技能时,目标返回以下一项或多项内容:
  • 面向用户的极简分步集成计划
  • 相关API的扁平化列表
  • 请求与响应字段分解
  • 用户技术栈对应的回调验证代码
  • curl
    fetch
    或TypeScript示例片段
  • 按会话状态分类的故障排除列表

Resources

资源

  • references/api-contract.md
    Use for bearer auth, endpoint contract, callback headers, payload fields, and third-party implementation shape.
  • references/checkout-and-renewal.md
    Use only as optional background for the high-level checkout lifecycle and renewal behavior.
  • references/discount-code-examples.md
    Example prompts, parameter cheatsheet, and ref-code usage for the Discount Code APIs.
  • scripts/sign_callback.py
    Use when you need a deterministic example of Portaly callback signing and verification.
  • scripts/sign_callback.mjs
    Prefer this for Node.js, JavaScript, TypeScript, Express, or Next.js integrations.
  • references/api-contract.md
    用于获取Bearer认证、端点约定、回调头、负载字段和第三方实现结构。
  • references/checkout-and-renewal.md
    仅作为结账生命周期和续费行为的可选背景资料。
  • references/discount-code-examples.md
    折扣码API的示例提示、参数速查表和推荐码用法。
  • scripts/sign_callback.py
    用于获取Portaly回调签名和验证的确定性示例。
  • scripts/sign_callback.mjs
    优先用于Node.js、JavaScript、TypeScript、Express或Next.js集成。