Skip to content

Commit 686f798

Browse files
author
Ian Walls
committed
Issue #359: update webhook receiving from Stripe
Process the webhook return value for successful, failed and cancelled payment intents. Marks the transaction, which should instantly make the pending funds usable. Errors are mostly logged, rather than returned to Stripe, since it's just giving us a notification
1 parent 42c4ebd commit 686f798

2 files changed

Lines changed: 65 additions & 19 deletions

File tree

lib/Libki/Controller/API/V2/Transactions/Stripe.pm

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package Libki::Controller::API::V2::Transactions::Stripe;
22

33
use Moose;
44
use namespace::autoclean;
5+
use JSON qw(decode_json);
56

67
use Libki::Payments::Stripe;
78

@@ -60,17 +61,36 @@ sub checkout : POST Path('checkout') Args(0) {
6061
6162
/api/v2/transactions/stripe/webhook
6263
63-
Returns 200 status on success
64+
Accepts update on payment intent status
6465
6566
=cut
6667

6768
sub webhook : POST Path('webhook') Args(0) {
6869
my ( $self, $c ) = @_;
6970

71+
my $body_fh = $c->request->body;
72+
my $payload = do { local $/; <$body_fh> };
73+
74+
my $json_data;
75+
eval {
76+
$json_data = decode_json($payload);
77+
1;
78+
} or do {
79+
$c->log->error('Stripe webhook: invalid JSON');
80+
$c->response->status(400);
81+
$c->response->body('invalid payload');
82+
return;
83+
};
84+
7085
my $provider = Libki::Payments::Stripe->new;
71-
$provider->handle_webhook( c => $c );
86+
$provider->handle_webhook(
87+
c => $c,
88+
data => $json_data,
89+
);
7290

7391
$c->response->status(200);
92+
$c->response->content_type('text/plain');
93+
$c->response->body('ok');
7494
}
7595

7696
__PACKAGE__->meta->make_immutable;

lib/Libki/Payments/Stripe.pm

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package Libki::Payments::Stripe;
22

33
use Moose;
44
use namespace::autoclean;
5+
use Data::Dumper;
56

67
with 'Libki::Payments::Provider';
78

@@ -72,35 +73,60 @@ sub create_checkout {
7273
=head2 handle_webhook
7374
7475
Receives incoming webhook call from Stripe with payload and signature.
75-
Updates transaction from pending to successful
76+
Updates transaction from pending to successful or failed
7677
7778
=cut
7879

7980
sub handle_webhook {
80-
require Net::Stripe;
81-
8281
my ( $self, %args ) = @_;
8382

84-
my $c = $args{c};
83+
my $data = $args{data};
84+
my $c = $args{c};
8585

86-
my $payload = $c->request->body_data;
87-
my $signature = $c->request->header('Stripe-Signature');
86+
my $event_type = $data->{type} // '';
87+
my $object = $data->{data}{object} // {};
8888

89-
my $event = Net::Stripe::Webhook::construct_event(
90-
$payload,
91-
$signature,
92-
$c->setting('StripeWebhookSigningSecret')
93-
);
89+
return 1 unless $object->{id};
90+
91+
my $pi_id = $object->{id};
92+
93+
my $txn = $c->model('DB::Transaction')->find({
94+
provider => 'stripe',
95+
provider_payment_id => $pi_id,
96+
});
97+
98+
unless ($txn) {
99+
$c->log->warn("Stripe webhook: no transaction for $pi_id");
100+
return 1;
101+
}
102+
103+
# Idempotency: don't touch terminal states
104+
return 1 if $txn->status =~ /^(succeeded|failed|cancelled)$/;
94105

95-
if ( $event->type eq 'checkout.session.completed' ) {
96-
my $session = $event->data->object;
106+
if ( $event_type eq 'payment_intent.succeeded' ) {
97107

98-
my $txn = $c->model('DB::Transaction')->find({
99-
provider => 'stripe',
100-
provider_payment_id => $session->id,
108+
$txn->update({
109+
status => 'succeeded',
101110
});
102111

103-
$txn->update({ status => 'succeeded' }) if $txn;
112+
}
113+
elsif ( $event_type eq 'payment_intent.payment_failed' ) {
114+
115+
my $reason =
116+
$object->{last_payment_error}{message}
117+
|| 'Payment failed';
118+
119+
$txn->update({
120+
status => 'failed',
121+
failure_reason => $reason,
122+
});
123+
124+
}
125+
elsif ( $event_type eq 'payment_intent.canceled' ) {
126+
127+
$txn->update({
128+
status => 'cancelled',
129+
});
104130
}
105131

106132
return 1;

0 commit comments

Comments
 (0)