用于测试的虚拟支付网关:Casdoor 的 Dummy 提供商、Stripe 测试模式和 PayPal 沙箱
虚拟支付网关是一种接受每笔结账为已付款而不联系银行或卡组织的支付提供商,因此你可以在没有资金和没有网关账户的情况下测试订单、订阅和履行。Casdoor 内置了一个:Dummy payment provider。将其添加到产品中,购买该产品,Casdoor 会将支付和订单记录为 Paid,启动订阅并写入交易,所有这些都无需离开你的服务器。在开发中使用 Dummy,并在预发布环境中切换到供应商沙箱(Stripe 测试模式、PayPal 沙箱、Paddle 沙箱),在那里你还需要处理被拒绝的卡片、3-D Secure 和真实的重定向。
Dummy、Balance 和供应商沙箱概览
| Provider | Test environment | How Casdoor picks test or live | Casdoor provider type |
|---|---|---|---|
| Dummy | 无需环境,每笔支付都成功 | 始终模拟 | Dummy |
| Balance | 内部,由充值产品提供资金 | 始终内部 | Balance |
| Stripe | 使用 sk_test_ / pk_test_ 密钥的沙箱 | 根据输入的密钥 | Stripe |
| PayPal | 沙箱应用和沙箱账户 | runmode 不为 prod 时使用沙箱 | PayPal |
| Paddle | 在 sandbox-vendors.paddle.com 的独立沙箱账户 | runmode 不为 prod 时使用 sandbox-api.paddle.com | Paddle |
| Adyen | 测试环境 (checkout-test.adyen.com) | runmode 不为 prod 时使用测试环境 | Adyen |
| AirWallex | Separate sandbox account | runmode other than prod calls api.sandbox.airwallex.com | AirWallex |
| Lemon Squeezy | 带有测试模式 API 密钥的商店测试模式 | 根据输入的 API 密钥 | Lemon Squeezy |
| FastSpring | 主机中包含 .test. 的测试店面 | 根据输入的 Host | FastSpring |
| Polar | sandbox.polar.sh 的沙箱环境 | 将提供商的 Host 设置为 https://sandbox-api.polar.sh | Polar |
runmode 是 conf/app.conf 中的选项(或 runmode 环境变量),在配置参考中有描述。默认值为 dev,因此新的 Casdoor 实例在你将其设置为 prod 之前,会与 PayPal、Paddle、Adyen 和 AirWallex 的沙箱通信。
虚拟交易的含义
虚拟交易运行与真实购买相同的记录(订单、支付、订阅、账本条目),但不移动资金。它与沙箱交易不同。沙箱仍然调用供应商的 API,显示供应商的结账页面并发送 webhook;只有资金是假的。虚拟交易永远不会离开你的系统。
这种差异决定了各自可以测试的内容。虚拟网关适合测试你自己的逻辑:计划是否授予正确的角色,订单是否出现,优惠券是否计为已使用,用户是否落在正确的页面上。沙箱适合测试集成:凭证、重定向、卡片拒绝、身份验证挑战和 webhook 交付。在不同阶段你需要两者。
Casdoor Dummy 提供商的工作原理
Dummy 提供商没有设置项,也没有支付页面。以下是 Casdoor master 分支上的完整流程:
- 当用户付款时,提供商的
Pay调用会返回 Casdoor 结果页面作为支付 URL。它将价格和货币打包到订单 ID 中,而不是在供应商处创建任何内容。 - Casdoor 将支付状态存储为
Created,并直接将浏览器重定向到结果页面。这里没有卡片表单,无论是真实的还是模拟的。 - 结果页面检测到状态为
Created的支付,并在大约一秒后调用POST /api/notify-payment。 - 提供商的
Notify调用始终回答Paid,并携带之前打包的价格,因此价格检查通过。 - 随后 Casdoor 执行正常的成功路径:支付和订单状态变为
Paid,为该支付创建的订阅被更新,写入一笔Purchase交易,产品库存减少,并且订单上的优惠券被标记为已使用。如果产品是充值产品,还会写入一笔Recharge交易,将金额添加到用户的余额中。
所以是的,Dummy 会立即将订单标记为已支付,用户会在中间没有任何步骤的情况下看到成功页面。
这种设计带来了两个限制。Dummy 不会失败:没有办法让它返回 Canceled、Timeout 或 Error,因此失败路径需要沙箱环境。而且 Dummy 不能续订:Casdoor 仅允许 Balance 和 Stripe 进行自动续订,因此使用 Dummy 购买的订阅总是关闭自动续订功能。
在新的数据库中,Casdoor 会在 admin 组织中创建一个名为 provider_payment_dummy 的内置 Dummy 提供商,旁边还有一个内置的 Balance 提供商。你也可以添加自己的提供商:在 Identity > Providers 中,将 Category 设置为 Payment,将 Type 设置为 Dummy。
请勿在生产环境中使用 Dummy
/api/notify-payment is open to anonymous callers, because real gateways call it without a Casdoor session. For a real provider that is safe, since Notify checks the payment with the vendor. Dummy checks nothing and reports Paid, so for a Dummy payment Casdoor only accepts the call from the payment's own user or an admin of its organization. That still leaves the main risk: any product that lists Dummy in its Payment providers shows it at checkout, and any signed-in user who picks it gets the product for free. Remove Dummy from every product before you go live, and do not put it on products in an organization that real users can reach.
Balance:可以使用 Dummy 充值的内部钱包
Balance 提供商 允许用户使用其账户余额付款。Casdoor 首先检查余额,扣除价格,并在同一请求中将支付和订单标记为 Paid。Balance 是一个真实的生产功能,不是测试工具,但它与 Dummy 配合得很好:将 Dummy 放在充值产品上,“充值”一个测试用户,余额就会增加,而不涉及实际资金。
这对订阅很重要,因为 Balance 是支持自动续订的两个提供商之一。结合使用 Dummy 和 Balance,你可以在笔记本电脑上测试整个续订循环,而无需网关账户。
用于预发布环境的供应商沙箱
Stripe。 Stripe 通过密钥区分测试数据和生产数据:沙箱密钥以 pk_test_ 和 sk_test_ 开头,生产密钥以 pk_live_ 和 sk_live_ 开头,且一种模式下的对象在另一种模式下不可见。Casdoor 使用你在 Stripe 提供商 中设置的任何密钥;runmode 不会改变 Stripe 的任何行为。Stripe 的测试卡覆盖了 Dummy 无法覆盖的路径:4242 4242 4242 4242 成功,4000 0000 0000 0002 被拒绝,4000 0000 0000 3220 需要 3-D Secure。使用任何未来的过期日期和任何 CVC。Stripe 的服务条款禁止在生产模式下使用真实卡详细信息进行测试。
PayPal。 PayPal 沙箱是一个独立的环境,拥有自己的应用和测试账户。当你首次登录开发者仪表板时,PayPal 会创建一个默认的个人账户和一个默认的商家沙箱账户;你使用个人账户进行支付。在沙箱模式下创建应用,将其凭据填入 PayPal 提供商,并保持 runmode 为 dev。
Paddle。 沙箱是位于 sandbox-vendors.paddle.com 的一个独立账户,拥有自己的密钥和产品;与生产环境不共享任何内容。Paddle 的测试卡包括 4242 4242 4242 4242(成功)和 4000 0000 0000 0002(被拒绝)。
Lemon Squeezy has a test mode per store, and API keys created in test mode only see test data. FastSpring test orders go through a test storefront: insert .test. into the store host, for example yourstore.test.onfastspring.com, and enter that as the provider's Host. Polar has a sandbox with its own tokens; set the provider's Host to https://sandbox-api.polar.sh to use it, and leave Host empty for live Polar. Alipay in Casdoor is also hard-wired to production.
Casdoor 产品、计划和订阅的测试计划
开发阶段,使用 Dummy
- 创建一个 产品 并将 Dummy 添加到其 Payment providers 中。
- 为该产品创建一个 计划 和一个列出该计划的定价页面(参见 定价概览)。
- 作为测试用户,打开定价页面并购买该计划。检查支付和订单是否为
Paid,订阅是否为Active,以及用户身上是否出现了一笔Purchase交易。 - 以该用户身份登录,并检查计划的角色在你的应用中是否生效。
- 如果计划启用了 Is exclusive,再次购买它,预期会出现用户已有活动订阅的错误。
- 在结账时应用优惠券,并检查其使用情况是否被记录。
- 创建一个带有 Dummy 的充值产品,为测试用户充值,然后使用 Balance 并开启 Auto renew 购买该计划。
- 为了在不等待一个月的情况下测试续订,请在订阅的编辑页面上修改其 End time,使其在 24 小时内结束。Casdoor 每小时检查一次订阅;预期 Renewal state 为
Renewed,并为下一周期生成一个新订阅。然后使用余额不足以支付下一周期的情况再次购买该计划,以相同方式移动其结束时间,预期结果为Failed,原因显示在 Renewal message 中。订阅文档 描述了确切的规则。
预发布阶段,使用沙箱
- 使用
runmode = dev运行预发布环境,并仅使用沙箱或测试凭据。 - 将 Dummy 替换为你将在生产环境中使用的提供商,并配置其沙箱密钥。
- 重复购买操作,检查浏览器是否跳转到供应商的结账页面,然后返回 Casdoor 结果页面。
- 对于 Stripe,分别使用拒绝卡和 3-D Secure 卡各支付一次,并检查每种情况下的付款和订单状态。
- 对于 Stripe 且开启 Auto renew 时,Casdoor 会在结账时保存卡片,并在续订时自行扣款,因此上述结束时间技巧在沙箱环境中同样有效。
上线
从所有产品中移除 Dummy,如果你使用 PayPal、Paddle、Adyen 或 AirWallex,请设置 runmode = prod,并切换为正式密钥。检查首批真实订单的付款记录。
如果在同一环境中也测试注册邮件,本地邮件捕获器可以防止这些消息发送到真实收件箱;请参阅 MailHog vs Mailpit。
参考资料
- Casdoor 源码:
pp/dummy.go、pp/balance.go、object/order_pay.go、object/payment.go、object/subscription_renew.go - Casdoor 文档:Payment providers、Dummy、Balance、Subscription
- Stripe:Testing、API keys
- PayPal:Sandbox、Sandbox accounts
- Paddle:Sandbox
- Lemon Squeezy:Test mode
- FastSpring:Test orders
- Polar:Sandbox
- Adyen:Live endpoints