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
108 lines
2.8 KiB
Go
108 lines
2.8 KiB
Go
package stripe
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"time"
|
|
|
|
"github.com/formancehq/go-libs/v3/currency"
|
|
"github.com/formancehq/payments/internal/connectors/plugins/public/stripe/client"
|
|
"github.com/formancehq/payments/internal/models"
|
|
"github.com/stripe/stripe-go/v80"
|
|
)
|
|
|
|
var (
|
|
legacyRootAccountReference = "root"
|
|
)
|
|
|
|
// we used to create a dummy ID for the root account which we don't want to pass to Stripe API clients
|
|
func resolveAccount(ref string) string {
|
|
if ref == legacyRootAccountReference {
|
|
return ""
|
|
}
|
|
return ref
|
|
}
|
|
|
|
type accountsState struct {
|
|
RootCreated bool `json:"root_created"`
|
|
Timeline client.Timeline `json:"timeline"`
|
|
}
|
|
|
|
func (p *Plugin) fetchNextAccounts(ctx context.Context, req models.FetchNextAccountsRequest) (models.FetchNextAccountsResponse, error) {
|
|
var oldState accountsState
|
|
if req.State != nil {
|
|
if err := json.Unmarshal(req.State, &oldState); err != nil {
|
|
return models.FetchNextAccountsResponse{}, err
|
|
}
|
|
}
|
|
|
|
accounts := make([]models.PSPAccount, 0, req.PageSize)
|
|
if !oldState.RootCreated {
|
|
// create a root account if this is the first time this is being run
|
|
rootAccount, err := p.client.GetRootAccount()
|
|
if err != nil {
|
|
return models.FetchNextAccountsResponse{}, err
|
|
}
|
|
pspAccount, err := ToPSPAccount(*rootAccount)
|
|
if err != nil {
|
|
return models.FetchNextAccountsResponse{}, err
|
|
}
|
|
accounts = append(accounts, pspAccount)
|
|
oldState.RootCreated = true
|
|
}
|
|
|
|
needed := req.PageSize - len(accounts)
|
|
|
|
newState := oldState
|
|
rawAccounts, timeline, hasMore, err := p.client.GetAccounts(ctx, oldState.Timeline, int64(needed))
|
|
if err != nil {
|
|
return models.FetchNextAccountsResponse{}, err
|
|
}
|
|
newState.Timeline = timeline
|
|
|
|
for _, acc := range rawAccounts {
|
|
pspAccount, err := ToPSPAccount(*acc)
|
|
if err != nil {
|
|
return models.FetchNextAccountsResponse{}, err
|
|
}
|
|
accounts = append(accounts, pspAccount)
|
|
}
|
|
|
|
payload, err := json.Marshal(newState)
|
|
if err != nil {
|
|
return models.FetchNextAccountsResponse{}, err
|
|
}
|
|
return models.FetchNextAccountsResponse{
|
|
Accounts: accounts,
|
|
NewState: payload,
|
|
HasMore: hasMore,
|
|
}, nil
|
|
}
|
|
|
|
func ToPSPAccount(acc stripe.Account) (models.PSPAccount, error) {
|
|
raw, err := json.Marshal(acc)
|
|
if err != nil {
|
|
return models.PSPAccount{}, err
|
|
}
|
|
|
|
metadata := make(map[string]string)
|
|
for k, v := range acc.Metadata {
|
|
metadata[k] = v
|
|
}
|
|
|
|
var displayName string
|
|
if acc.Settings != nil && acc.Settings.Dashboard != nil {
|
|
displayName = acc.Settings.Dashboard.DisplayName
|
|
}
|
|
|
|
defaultAsset := currency.FormatAsset(supportedCurrenciesWithDecimal, string(acc.DefaultCurrency))
|
|
return models.PSPAccount{
|
|
Name: &displayName,
|
|
Reference: acc.ID,
|
|
CreatedAt: time.Unix(acc.Created, 0).UTC(),
|
|
DefaultAsset: &defaultAsset,
|
|
Raw: raw,
|
|
Metadata: metadata,
|
|
}, nil
|
|
}
|