From ecf3eeca33dfcce5632bc16b0cc25ef76661c259 Mon Sep 17 00:00:00 2001 From: Dan Schmidt Date: Mon, 13 Jul 2015 12:39:48 -0700 Subject: [PATCH] Support Transfer Reversals (#20) Adds support for reversing existing transfers via a new create_reversal method. This method accepts three arguments: - (Required) The transfer ID - any stringifiable value - (Optional) Reversal data - a hashref defaulting to {} - (Optional) Options - a hashref defaulting to {}. Right now the only valid key is 'headers', which is passed verbatim to the LWP::UserAgent as request headers. The method signature ($id, $data = {}, $opts = {}) is meant as a template for all methods that act on a single, existing object. Right now, WS::Stripe uses a mix of positional and named parameters that is not consistent and requires looking back at the documentation. We should unify these signatures to promote understanding. For operations involving new objects, I'd propose the very-similar signature ($data, $opts = {}). --- Makefile.PL | 4 +- README.md | 22 ++++++++++- dist.ini | 2 +- lib/WebService/Stripe.pm | 34 +++++++++++++++++ t/04-transfers.t | 80 ++++++++++++++++++++++++++++++++++------ 5 files changed, 127 insertions(+), 15 deletions(-) diff --git a/Makefile.PL b/Makefile.PL index c91c345..7786e39 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -1,4 +1,4 @@ -# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.037. +# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.036. use strict; use warnings; @@ -36,7 +36,7 @@ my %WriteMakefileArgs = ( "strict" => 0, "warnings" => 0 }, - "VERSION" => "0.1100", + "VERSION" => "0.1200", "test" => { "TESTS" => "t/*.t" } diff --git a/README.md b/README.md index 59d5e6a..06d5f14 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ WebService::Stripe - Stripe API bindings # VERSION -version 0.1100 +version 0.1200 # SYNOPSIS @@ -243,6 +243,26 @@ Example: cancel_transfer($id) +## create\_reversal + +Reverses an existing transfer. + +Example: + + $ws_stripe->create_reversal( + # Transfer ID (required) + $xfer_id, + { # POST data (optional) + refund_application_fee => 'true' + }, + { # Request options (optional) + stripe_account => $account->{'id'}, + headers => { + 'Other-Header' => 'Foo' + } + } + ); + ## get\_balance get_balance() diff --git a/dist.ini b/dist.ini index 086574e..00dfee8 100644 --- a/dist.ini +++ b/dist.ini @@ -5,7 +5,7 @@ license = Perl_5 copyright_holder = Tilt, Inc copyright_year = 2014 -version = 0.1100 +version = 0.1200 [@Filter] -bundle = @Basic diff --git a/lib/WebService/Stripe.pm b/lib/WebService/Stripe.pm index 1f8f42f..caaac8e 100644 --- a/lib/WebService/Stripe.pm +++ b/lib/WebService/Stripe.pm @@ -173,6 +173,12 @@ method cancel_transfer(Str $id, :$headers) { return $self->post("/v1/transfers/$id/cancel", undef, headers => $headers); } +method create_reversal($xfer_id, HashRef $data = {}, HashRef $opts = {}) { + return $self->post("/v1/transfers/$xfer_id/reversals", $data, + headers => $self->_stripe_opts_to_headers($opts), + ); +} + method get_bitcoin_receivers(HashRef :$query, :$headers) { return $self->get( "/v1/bitcoin/receivers", $query, headers => $headers ); } @@ -185,6 +191,14 @@ method get_bitcoin_receiver(Str $id, :$headers) { return $self->get( "/v1/bitcoin/receivers/$id", {}, headers => $headers ); } +method _stripe_opts_to_headers(HashRef $opts) { + my $headers = $opts->{'headers'} // {}; + if ($opts->{'stripe_account'}) { + $headers->{'Stripe-Account'} = $opts->{'stripe_account'}; + } + return $headers; +} + # ABSTRACT: Stripe API bindings =head1 SYNOPSIS @@ -428,6 +442,26 @@ Example: cancel_transfer($id) +=head2 create_reversal + +Reverses an existing transfer. + +Example: + + $ws_stripe->create_reversal( + # Transfer ID (required) + $xfer_id, + { # POST data (optional) + refund_application_fee => 'true' + }, + { # Request options (optional) + stripe_account => $account->{'id'}, + headers => { + 'Other-Header' => 'Foo' + } + } + ); + =head2 get_balance get_balance() diff --git a/t/04-transfers.t b/t/04-transfers.t index b7028ff..fc65097 100644 --- a/t/04-transfers.t +++ b/t/04-transfers.t @@ -1,4 +1,4 @@ -use Test::Modern; +use Test::Modern qw(:deeper :fatal :more); use t::lib::Common qw(:constants skip_unless_has_secret stripe); use JSON qw(from_json); @@ -17,7 +17,7 @@ my $bank = stripe->add_bank( }, account_id => $account->{id}, ); -cmp_deeply $bank => TD->superhashof({ last4 => 6789 }), 'created bank'; +cmp_deeply $bank => superhashof({ last4 => 6789 }), 'created bank'; subtest 'create a transfer and do stuff with it' => sub { my $transfer = stripe->create_transfer({ @@ -25,26 +25,31 @@ subtest 'create a transfer and do stuff with it' => sub { currency => 'cad', destination => $account->{id}, }); - cmp_deeply $transfer => TD->superhashof({ - id => TD->re('^tr_'), + cmp_deeply $transfer => superhashof({ + id => re('^tr_'), amount => 100, - }); - my $transfer_id = $transfer->{id}; + }), + '... Created a transfer'; + my $transfer_id = $transfer->{id}; $transfer = stripe->update_transfer($transfer->{id}, data => { 'metadata[foo]' => 'bar' }); - is $transfer->{id} => $transfer_id; + is $transfer->{id}, $transfer_id, + '... Updated a transfer'; $transfer = stripe->get_transfer($transfer->{id}); - cmp_deeply $transfer => TD->superhashof({ + cmp_deeply $transfer => superhashof({ id => $transfer_id, amount => 100, metadata => { foo => 'bar' }, - }); + }), + '... Got an existing transfer'; - my $exc = exception { stripe->cancel_transfer($transfer->{id}) }; - is $exc->code => 400; + # Expect failure b/c Stripe's test env doesn't support this + my $err = exception { stripe->cancel_transfer($transfer->{id}) }; + like $err, qr/while they are pending/, + '... Sent cancel request to Stripe'; }; subtest 'list transfers' => sub { @@ -52,4 +57,57 @@ subtest 'list transfers' => sub { ok $transfers->{data}[0]{amount}; }; +subtest 'create_reversal' => sub { + subtest "Can create a complete reversal" => sub { + my $xfer = stripe->create_transfer({ + amount => 100, + currency => 'cad', + destination => $account->{'id'}, + 'metadata[tester]' => 'WebService::Stripe::create_reversal', + }); + + my $reversal = stripe->create_reversal($xfer->{'id'}); + cmp_deeply $reversal, superhashof({ + object => 'transfer_reversal', + amount => 100, + }), + '... Created a full reversal', + or diag explain $reversal; + }; + + subtest "Can create a partial reversal" => sub { + my $xfer = stripe->create_transfer({ + amount => 50, + currency => 'cad', + destination => $account->{'id'}, + }); + + my $reversal = stripe->create_reversal($xfer->{'id'}); + cmp_deeply $reversal, superhashof({ + object => 'transfer_reversal', + amount => 50, + }), + '... Created a 50% reversal', + or diag explain $reversal; + }; + + subtest "Can reverse an Account-scoped bank transfer" => sub { + my $xfer = stripe->create_transfer({ + amount => 50, + currency => 'cad', + destination => $bank->{'id'}, + }, headers => { stripe_account => $account->{'id'} }); + + my $err = exception { + stripe->create_reversal($xfer->{'id'}, { + amount => 25, + }, { stripe_account => $account->{'id'} }); + }; + + # Expect failure b/c Stripe's test environment doesn't support this + like $err, qr/while they are pending/, + '... Sent reversal request to Stripe'; + }; +}; + done_testing;