mirror of
https://github.com/hanzo-fi/payments.git
synced 2026-10-30 20:31:20 +00:00
* Implement Balances Webhooks in Stripe to reduce polling workflows * Create webhooks on install * Remove webhook at uninstall time * Add verifyWebhook function to Stripe * Pin APIVersion in Stripe Webhook Create call * Default to root account when receiving balance.update webhook * Only make call do delete webhooks if webhooks were setup to begin with * Set up two webhook endpoints - one for connect and one for root account * Use event.Created to determine balance time in Stripe webhooks * Start recording root account ID instead of dummy 'root' value * Create unit test for Stripe create webhooks * Add tests for Stripe Translate webhook * Share logic between Stripe balance polling and webhook handle * Use supported webhooks map * Update existing stripe webhooks instead of creating new ones at app start time * Run install sequence for each connector at app start of payments api pods * Turn of balances periodically for Stripe * Add nil checks for balance.Available * Remove potentially sensitive information from error message * Add migration tool to remove FETCH BALANCES schedules from stripe
127 lines
3.2 KiB
Go
127 lines
3.2 KiB
Go
package client
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/stripe/stripe-go/v80"
|
|
)
|
|
|
|
// Timeline allows the client to navigate the backlog and decide whether to fetch
|
|
// historical or recently added data
|
|
type Timeline struct {
|
|
LatestID string `json:"latest_id"`
|
|
BacklogCursor string `json:"backlog_cursor"`
|
|
BacklogStartingPoint string `json:"backlog_starting_point,omitempty"` // used by users of fetchBacklog
|
|
}
|
|
|
|
func (t Timeline) IsCaughtUp() bool {
|
|
return t.LatestID != ""
|
|
}
|
|
|
|
// used to find oldest transaction since we can only import them in chronological order
|
|
func scanForOldest(
|
|
timeline Timeline,
|
|
pageSize int64,
|
|
listFn func(stripe.ListParams) (stripe.ListContainer, error),
|
|
) (interface{}, Timeline, bool, error) {
|
|
filters := stripe.ListParams{
|
|
Limit: limit(pageSize, 0),
|
|
Single: true, // turn off autopagination
|
|
}
|
|
if timeline.BacklogCursor != "" {
|
|
filters.StartingAfter = &timeline.BacklogCursor
|
|
}
|
|
|
|
var oldest interface{}
|
|
var oldestID string
|
|
|
|
list, err := listFn(filters)
|
|
if err != nil {
|
|
return oldest, timeline, false, err
|
|
}
|
|
hasMore := list.GetListMeta().HasMore
|
|
|
|
switch v := list.(type) {
|
|
case *stripe.BalanceTransactionList:
|
|
if len(v.Data) == 0 {
|
|
return oldest, timeline, hasMore, nil
|
|
}
|
|
trx := v.Data[len(v.Data)-1]
|
|
oldest = trx
|
|
oldestID = trx.ID
|
|
default:
|
|
return nil, timeline, hasMore, fmt.Errorf("failed to scan for oldest for type %T", list)
|
|
}
|
|
|
|
// we haven't found the oldest yet
|
|
if hasMore {
|
|
timeline.BacklogCursor = oldestID
|
|
return nil, timeline, hasMore, nil
|
|
}
|
|
timeline.LatestID = oldestID
|
|
return oldest, timeline, hasMore, nil
|
|
}
|
|
|
|
// fetchBacklog receives records we don't need to fetch in chronological order
|
|
func fetchBacklog(
|
|
timeline Timeline,
|
|
pageSize int64,
|
|
listFn func(stripe.ListParams) (stripe.ListContainer, error),
|
|
) ([]interface{}, Timeline, bool, error) {
|
|
filters := stripe.ListParams{
|
|
Limit: limit(pageSize, 0),
|
|
Single: true, // turn off autopagination
|
|
}
|
|
if timeline.BacklogCursor != "" {
|
|
filters.StartingAfter = &timeline.BacklogCursor
|
|
}
|
|
|
|
results := make([]interface{}, 0, pageSize)
|
|
var oldestID, newestID string
|
|
|
|
list, err := listFn(filters)
|
|
if err != nil {
|
|
return results, timeline, false, err
|
|
}
|
|
hasMore := list.GetListMeta().HasMore
|
|
|
|
switch v := list.(type) {
|
|
case *stripe.AccountList:
|
|
if len(v.Data) == 0 {
|
|
return results, timeline, hasMore, nil
|
|
}
|
|
account := v.Data[len(v.Data)-1]
|
|
oldestID = account.ID
|
|
newestID = v.Data[0].ID
|
|
for _, acc := range v.Data {
|
|
results = append(results, acc)
|
|
}
|
|
|
|
case *stripe.BankAccountList:
|
|
if len(v.Data) == 0 {
|
|
return results, timeline, hasMore, nil
|
|
}
|
|
account := v.Data[len(v.Data)-1]
|
|
oldestID = account.ID
|
|
newestID = v.Data[0].ID
|
|
for _, acc := range v.Data {
|
|
results = append(results, acc)
|
|
}
|
|
default:
|
|
return results, timeline, hasMore, fmt.Errorf("failed to fetch backlog for type %T", list)
|
|
}
|
|
|
|
// on the very first run we keep track of where we started
|
|
if timeline.BacklogStartingPoint == "" {
|
|
timeline.BacklogStartingPoint = newestID
|
|
}
|
|
|
|
// we haven't found the oldest yet
|
|
if hasMore {
|
|
timeline.BacklogCursor = oldestID
|
|
} else {
|
|
timeline.LatestID = timeline.BacklogStartingPoint
|
|
}
|
|
return results, timeline, hasMore, nil
|
|
}
|