How can I test unpaid subscriptions in Stripe with Ruby on Rails?

I try to test the script in my Rails application when the client allowed the subscription to go “unpaid” (usually because the card expired and did not renew for two weeks, and Stripe repeated the card) and finally bypass the updated card and activate an account. I believe that I have the correct logic (update the card and pay each unpaid invoice), but I would like to check it or, even better, write some RSpec tests (function test and, possibly, controller test). The problem is that I cannot figure out how to create a layout in which the subscription is “unpaid”.(I suppose I could create a bunch of accounts with expired cards and wait two weeks for the test, but this is not an acceptable solution. I can't even change the re-subscription settings for the "test" context to speed things up.) I found stripe-ruby -mock , but I can’t manually set the subscription status.

Here is what I tried:

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'

This was the result with stripe-ruby-mock:

Failure/Error: expect(sub.status).to eq 'unpaid'

   expected: "unpaid"
        got: "active"
+4
source share
2 answers

Recommended lane:

  • Configure the client using the card 4000000000000341 ("Attaching this card to the client object will succeed, but attempts to charge the client will fail.")

  • , , /.

  • Wait

, .

+6

@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 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

- . , , , ruby, ( ):

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

javascript, phantomjs (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>

, , , ! :)

+2

All Articles