├── .gitignore ├── Readme.md ├── hooks.go ├── hooks_test.go └── types.go /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | scrape.js 3 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # go-stripehooks 2 | 3 | Stripe Webhook manager with verification support. 4 | 5 | ## Badges 6 | 7 | [![Build Status](https://semaphoreci.com/api/v1/tj/go-stripehooks/branches/master/badge.svg)](https://semaphoreci.com/tj/go-stripehooks) 8 | [![GoDoc](https://godoc.org/github.com/tj/go-stripehooks?status.svg)](https://godoc.org/github.com/tj/go-stripehooks) 9 | ![](https://img.shields.io/badge/license-MIT-blue.svg) 10 | ![](https://img.shields.io/badge/status-stable-green.svg) 11 | [![](http://apex.sh/images/badge.svg)](https://apex.sh/ping/) 12 | 13 | --- 14 | 15 | > [tjholowaychuk.com](http://tjholowaychuk.com)  ·  16 | > GitHub [@tj](https://github.com/tj)  ·  17 | > Twitter [@tjholowaychuk](https://twitter.com/tjholowaychuk) 18 | -------------------------------------------------------------------------------- /hooks.go: -------------------------------------------------------------------------------- 1 | // Package stripehooks provides hook management with optional event fetching to verify that the origin is Stripe, this 2 | // functionality is defaulted to true. 3 | package stripehooks 4 | 5 | import ( 6 | stripe "github.com/stripe/stripe-go" 7 | "github.com/stripe/stripe-go/event" 8 | ) 9 | 10 | // EventType is the type of event for the hook. 11 | type EventType string 12 | 13 | // Handler is a Stripe event handler. 14 | type Handler interface { 15 | HandleStripeEvent(*stripe.Event) error 16 | } 17 | 18 | // HandlerFunc handles a stripe event. 19 | type HandlerFunc func(*stripe.Event) error 20 | 21 | // HandleStripeEvent implements Handler. 22 | func (h HandlerFunc) HandleStripeEvent(e *stripe.Event) error { 23 | return h(e) 24 | } 25 | 26 | // Manager manages Stripe hooks. 27 | type Manager struct { 28 | Verify bool // Verify the event by fetching from Stripe 29 | hooks map[EventType]Handler 30 | } 31 | 32 | // New hook manager. 33 | func New() *Manager { 34 | return &Manager{ 35 | Verify: true, 36 | hooks: make(map[EventType]Handler), 37 | } 38 | } 39 | 40 | // Handle registers an even handler. 41 | func (m *Manager) Handle(kind EventType, h Handler) { 42 | m.hooks[kind] = h 43 | } 44 | 45 | // HandleFunc registers an even handler. 46 | func (m *Manager) HandleFunc(kind EventType, h HandlerFunc) { 47 | m.hooks[kind] = h 48 | } 49 | 50 | // Registered returns true if a handler is registered. 51 | func (m *Manager) Registered(kind EventType) bool { 52 | _, ok := m.hooks[kind] 53 | return ok 54 | } 55 | 56 | // HandleEvent handles a Stripe event, no-oping if a handler 57 | // has not been defined. If Verify is true then the event is 58 | // fetched from Stripe to validate its origin. 59 | func (m *Manager) HandleEvent(e *stripe.Event) error { 60 | h, ok := m.hooks[EventType(e.Type)] 61 | if !ok { 62 | return nil 63 | } 64 | 65 | if m.Verify { 66 | verified, err := event.Get(e.ID, nil) 67 | if err != nil { 68 | return err 69 | } 70 | 71 | e = verified 72 | } 73 | 74 | return h.HandleStripeEvent(e) 75 | } 76 | -------------------------------------------------------------------------------- /hooks_test.go: -------------------------------------------------------------------------------- 1 | package stripehooks 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stripe/stripe-go" 8 | ) 9 | 10 | func handle(e *stripe.Event) error { 11 | return nil 12 | } 13 | 14 | func TestManager_Registered(t *testing.T) { 15 | m := New() 16 | assert.False(t, m.Registered(ChargeCaptured)) 17 | m.Handle(ChargeCaptured, HandlerFunc(handle)) 18 | assert.True(t, m.Registered(ChargeCaptured)) 19 | } 20 | 21 | func TestManager_HandleFunc(t *testing.T) { 22 | m := New() 23 | assert.False(t, m.Registered(ChargeCaptured)) 24 | m.HandleFunc(ChargeCaptured, handle) 25 | assert.True(t, m.Registered(ChargeCaptured)) 26 | } 27 | 28 | func TestManager_HandleEvent(t *testing.T) { 29 | m := New() 30 | m.Verify = false 31 | 32 | called := false 33 | 34 | m.HandleFunc(ChargeCaptured, func(e *stripe.Event) error { 35 | assert.Equal(t, "evt_18eWOZH5vuhJUCbaZ7He0cFF", e.ID) 36 | called = true 37 | return nil 38 | }) 39 | 40 | err := m.HandleEvent(&stripe.Event{ 41 | ID: "evt_18eWOZH5vuhJUCbaZ7He0cFF", 42 | Type: ChargeCaptured, 43 | }) 44 | 45 | assert.NoError(t, err) 46 | assert.True(t, called, "should invoke the handler") 47 | } 48 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | package stripehooks 2 | 3 | // Event types available. 4 | const ( 5 | AccountUpdated EventType = "account.updated" // Occurs whenever an account status or property has changed. 6 | AccountApplicationDeauthorized = "account.application.deauthorized" // Occurs whenever a user deauthorizes an application. Sent to the related application only. 7 | AccountExternalAccountCreated = "account.external_account.created" // Occurs whenever an external account is created. 8 | AccountExternalAccountDeleted = "account.external_account.deleted" // Occurs whenever an external account is deleted. 9 | AccountExternalAccountUpdated = "account.external_account.updated" // Occurs whenever an external account is updated. 10 | ApplicationFeeCreated = "application_fee.created" // Occurs whenever an application fee is created on a charge. 11 | ApplicationFeeRefunded = "application_fee.refunded" // Occurs whenever an application fee is refunded, whether from refunding a charge or from refunding the application fee directly, including partial refunds. 12 | ApplicationFeeRefundUpdated = "application_fee.refund.updated" // Occurs whenever an application fee refund is updated. 13 | BalanceAvailable = "balance.available" // Occurs whenever your Stripe balance has been updated (e.g. when a charge collected is available to be paid out). By default, Stripe will automatically transfer any funds in your balance to your bank account on a daily basis. 14 | BitcoinReceiverCreated = "bitcoin.receiver.created" // Occurs whenever a receiver has been created. 15 | BitcoinReceiverFilled = "bitcoin.receiver.filled" // Occurs whenever a receiver is filled (that is, when it has received enough bitcoin to process a payment of the same amount). 16 | BitcoinReceiverUpdated = "bitcoin.receiver.updated" // Occurs whenever a receiver is updated. 17 | BitcoinReceiverTransactionCreated = "bitcoin.receiver.transaction.created" // Occurs whenever bitcoin is pushed to a receiver. 18 | ChargeCaptured = "charge.captured" // Occurs whenever a previously uncaptured charge is captured. 19 | ChargeFailed = "charge.failed" // Occurs whenever a failed charge attempt occurs. 20 | ChargeRefunded = "charge.refunded" // Occurs whenever a charge is refunded, including partial refunds. 21 | ChargeSucceeded = "charge.succeeded" // Occurs whenever a new charge is created and is successful. 22 | ChargeUpdated = "charge.updated" // Occurs whenever a charge description or metadata is updated. 23 | ChargeDisputeClosed = "charge.dispute.closed" // Occurs when the dispute is closed and the dispute status changes to charge_refunded, lost, warning_closed, or won. 24 | ChargeDisputeCreated = "charge.dispute.created" // Occurs whenever a customer disputes a charge with their bank (chargeback). 25 | ChargeDisputeFundsReinstated = "charge.dispute.funds_reinstated" // Occurs when funds are reinstated to your account after a dispute is won. 26 | ChargeDisputeFundsWithdrawn = "charge.dispute.funds_withdrawn" // Occurs when funds are removed from your account due to a dispute. 27 | ChargeDisputeUpdated = "charge.dispute.updated" // Occurs when the dispute is updated (usually with evidence). 28 | CouponCreated = "coupon.created" // Occurs whenever a coupon is created. 29 | CouponDeleted = "coupon.deleted" // Occurs whenever a coupon is deleted. 30 | CouponUpdated = "coupon.updated" // Occurs whenever a coupon is updated. 31 | CustomerCreated = "customer.created" // Occurs whenever a new customer is created. 32 | CustomerDeleted = "customer.deleted" // Occurs whenever a customer is deleted. 33 | CustomerUpdated = "customer.updated" // Occurs whenever any property of a customer changes. 34 | CustomerDiscountCreated = "customer.discount.created" // Occurs whenever a coupon is attached to a customer. 35 | CustomerDiscountDeleted = "customer.discount.deleted" // Occurs whenever a customer's discount is removed. 36 | CustomerDiscountUpdated = "customer.discount.updated" // Occurs whenever a customer is switched from one coupon to another. 37 | CustomerSourceCreated = "customer.source.created" // Occurs whenever a new source is created for the customer. 38 | CustomerSourceDeleted = "customer.source.deleted" // Occurs whenever a source is removed from a customer. 39 | CustomerSourceUpdated = "customer.source.updated" // Occurs whenever a source's details are changed. 40 | CustomerSubscriptionCreated = "customer.subscription.created" // Occurs whenever a customer with no subscription is signed up for a plan. 41 | CustomerSubscriptionDeleted = "customer.subscription.deleted" // Occurs whenever a customer ends their subscription. 42 | CustomerSubscriptionTrialWillEnd = "customer.subscription.trial_will_end" // Occurs three days before the trial period of a subscription is scheduled to end. 43 | CustomerSubscriptionUpdated = "customer.subscription.updated" // Occurs whenever a subscription changes. Examples would include switching from one plan to another, or switching status from trial to active. 44 | InvoiceCreated = "invoice.created" // Occurs whenever a new invoice is created. If you are using webhooks, Stripe will wait one hour after they have all succeeded to attempt to pay the invoice; the only exception here is on the first invoice, which gets created and paid immediately when you subscribe a customer to a plan. If your webhooks do not all respond successfully, Stripe will continue retrying the webhooks every hour and will not attempt to pay the invoice. After 3 days, Stripe will attempt to pay the invoice regardless of whether or not your webhooks have succeeded. See how to respond to a webhook. 45 | InvoicePaymentFailed = "invoice.payment_failed" // Occurs whenever an invoice attempts to be paid, and the payment fails. This can occur either due to a declined payment, or because the customer has no active card. A particular case of note is that if a customer with no active card reaches the end of its free trial, an invoice.payment_failed notification will occur. 46 | InvoicePaymentSucceeded = "invoice.payment_succeeded" // Occurs whenever an invoice attempts to be paid, and the payment succeeds. 47 | InvoiceUpdated = "invoice.updated" // Occurs whenever an invoice changes (for example, the amount could change). 48 | InvoiceitemCreated = "invoiceitem.created" // Occurs whenever an invoice item is created. 49 | InvoiceitemDeleted = "invoiceitem.deleted" // Occurs whenever an invoice item is deleted. 50 | InvoiceitemUpdated = "invoiceitem.updated" // Occurs whenever an invoice item is updated. 51 | OrderCreated = "order.created" // Occurs whenever an order is created. 52 | OrderPaymentFailed = "order.payment_failed" // Occurs whenever payment is attempted on an order, and the payment fails. 53 | OrderPaymentSucceeded = "order.payment_succeeded" // Occurs whenever payment is attempted on an order, and the payment succeeds. 54 | OrderUpdated = "order.updated" // Occurs whenever an order is updated. 55 | OrderReturnCreated = "order_return.created" // Occurs whenever an order return created. 56 | PlanCreated = "plan.created" // Occurs whenever a plan is created. 57 | PlanDeleted = "plan.deleted" // Occurs whenever a plan is deleted. 58 | PlanUpdated = "plan.updated" // Occurs whenever a plan is updated. 59 | ProductCreated = "product.created" // Occurs whenever a product is created. 60 | ProductDeleted = "product.deleted" // Occurs whenever a product is deleted. 61 | ProductUpdated = "product.updated" // Occurs whenever a product is updated. 62 | RecipientCreated = "recipient.created" // Occurs whenever a recipient is created. 63 | RecipientDeleted = "recipient.deleted" // Occurs whenever a recipient is deleted. 64 | RecipientUpdated = "recipient.updated" // Occurs whenever a recipient is updated. 65 | SkuCreated = "sku.created" // Occurs whenever a SKU is created. 66 | SkuDeleted = "sku.deleted" // Occurs whenever a SKU is deleted. 67 | SkuUpdated = "sku.updated" // Occurs whenever a SKU is updated. 68 | TransferCreated = "transfer.created" // Occurs whenever a new transfer is created. 69 | TransferFailed = "transfer.failed" // Occurs whenever Stripe attempts to send a transfer and that transfer fails. 70 | TransferPaid = "transfer.paid" // Occurs whenever a sent transfer is expected to be available in the destination bank account. If the transfer failed, a transfer.failed webhook will additionally be sent at a later time. Note to Connect users: this event is only created for transfers from your connected Stripe accounts to their bank accounts, not for transfers to the connected accounts themselves. 71 | TransferReversed = "transfer.reversed" // Occurs whenever a transfer is reversed, including partial reversals. 72 | TransferUpdated = "transfer.updated" // Occurs whenever the description or metadata of a transfer is updated. 73 | Ping = "ping" // May be sent by Stripe at any time to see if a provided webhook URL is working. 74 | ) 75 | --------------------------------------------------------------------------------