我正在尝试在我的Rails应用程序中测试该方案,其中客户允许订阅转为“未付款”(通常是因为该卡过期并且在Stripe重试收费的两周内未更新)并且是最后到处更新卡并重新激活帐户。我相信我的逻辑是正确的(更新卡并支付每张未付的发票),但我希望能够测试它,或者更好的是,写一些RSpec测试(功能测试和可能的控制器测试)。 问题是,我无法弄清楚如何创建一个订阅“未付”的模拟情况。(我想我可以用过期的卡创建一堆帐户并等待两周到测试,但这不是一个可接受的解决方案。我甚至无法仅为“测试”上下文更改订阅重试设置以加快处理速度。)我找到stripe-ruby-mock但我无法手动设置状态订阅。
这是我尝试过的:
plan = Stripe::Plan.create(id: 'test')
customer = Stripe::Customer.create(id: 'test_customer', card: 'tk', plan: 'test')
sub = customer.subscriptions.retrieve(customer.subscriptions.data.first.id)
sub.status = 'unpaid'
sub.save
sub = customer.subscriptions.retrieve(customer.subscriptions.data.first.id)
expect(sub.status).to eq 'unpaid'
这是stripe-ruby-mock的结果:
Failure/Error: expect(sub.status).to eq 'unpaid'
expected: "unpaid"
got: "active"
答案 0 :(得分:8)
Stripe的推荐程序是:
使用卡4000000000000341设置客户("将此卡附加到客户对象将成功,但尝试向客户收费将失败。")
向客户提供订阅,但试用日期为今天/明天。
等待
第三步很烦人,但它会完成工作。
答案 1 :(得分:2)
考虑到@ VoteyDisciple的建议,我已经通过一些方法在RSpec中合理地自动化了(根据具体情况,合理地')。我使用VCR来捕获API调用(针对测试条带环境),这是必不可少的,因为这意味着只有在第一次记录测试时才会发生sleep
调用。
使用注释来表示通过Stripe的API完成的行为,因为我的实现很容易被我的项目所吸引,并且没那么有用。
VCR.use_cassette('retry') do |cassette|
# Clear out existing Stripe data: customers, coupons, plans.
# This is so the test is reliably repeatable.
Stripe::Customer.all.each &:delete
Stripe::Coupon.all.each &:delete
Stripe::Plan.all.each &:delete
# Create a plan, in my case it has the id 'test'.
Stripe::Plan.create(
id: 'test',
amount: 100_00,
currency: 'AUD',
interval: 'month',
interval_count: 1,
name: 'RSpec Test'
)
# Create a customer
customer = Stripe::Customer.create email: 'test@test.test'
token = card_token cassette, '4000000000000341'
# Add the card 4000000000000341 to the customer
customer.sources.create token: 'TOKEN for 0341'
# Create a subscription with a trial ending in two seconds.
subscription = customer.subscriptions.create(
plan: 'test',
trial_end: 2.seconds.from_now.to_i
)
# Wait for Stripe to create a proper invoice. I wish this
# was unavoidable, but I don't think it is.
sleep 180 if cassette.recording?
# Grab the invoice that actually has a dollar value.
# There's an invoice for the trial, and we don't care about that.
invoice = customer.invoices.detect { |invoice| invoice.total > 0 }
# Force Stripe to attempt payment for the first time (instead
# of waiting for hours).
begin
invoice.pay
rescue Stripe::CardError
# Expecting this to fail.
end
invoice.refresh
expect(invoice.paid).to eq(false)
expect(invoice.attempted).to eq(true)
# Add a new (valid) card to the customer.
token = card_token cassette, '4242424242424242'
card = customer.sources.create token: token
# and set it as the default
customer.default_source = card.id
customer.save
# Run the code in your app that retries the payment, which
# essentially invokes invoice.pay again.
# THIS IS FOR YOU TO IMPLEMENT
# And now we can check that the invoice wass successfully paid
invoice.refresh
expect(invoice.paid).to eq(true)
end
以自动方式获取卡片令牌是一个全新的复杂领域。我所得到的可能对其他人不起作用,但这里是红宝石方法,呼唤幻影(你需要发送录像机对象):
def card_token(cassette, card = '4242424242424242')
return 'tok_my_default_test_token' unless cassette.recording?
token = `phantomjs --ignore-ssl-errors=true --ssl-protocol=any ./spec/fixtures/stripe_tokens.js #{ENV['STRIPE_PUBLISH_KEY']} #{card}`.strip
raise "Unexpected token: #{token}" unless token[/^tok_/]
token
end
phantomjs正在运行的javascript文件(stripe_tokens.js)包含:
var page = require('webpage').create(),
system = require('system');
var key = system.args[1],
card = system.args[2];
page.onCallback = function(data) {
console.log(data);
phantom.exit();
};
page.open('spec/fixtures/stripe_tokens.html', function(status) {
if (status == 'success') {
page.evaluate(function(key, card) {
Stripe.setPublishableKey(key);
Stripe.card.createToken(
{number: card, cvc: "123", exp_month: "12", exp_year: "2019"},
function(status, response) { window.callPhantom(response.id) }
);
}, key, card);
}
});
最后,涉及的HTML文件(stripe_tokens.html)非常简单:
<html>
<head>
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
</head>
<body></body>
</html>
将所有这些放在一起,而且,它可能会起作用!它适用于我们的应用程序:)