Files
CrimsonandGitHub c48bb246b5 feat(Stripe): EN-470 Add balance.available webhooks to Stripe connector (#590)
* 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
2025-11-21 10:25:52 +00:00

52 lines
1.4 KiB
Go

package stripe
import (
"context"
"encoding/json"
"math/big"
"time"
"github.com/formancehq/go-libs/v3/currency"
"github.com/formancehq/payments/internal/models"
"github.com/pkg/errors"
"github.com/stripe/stripe-go/v80"
)
func (p *Plugin) fetchNextBalances(ctx context.Context, req models.FetchNextBalancesRequest) (models.FetchNextBalancesResponse, error) {
var from models.PSPAccount
if req.FromPayload == nil {
return models.FetchNextBalancesResponse{}, errors.New("missing from payload when fetching balances")
}
if err := json.Unmarshal(req.FromPayload, &from); err != nil {
return models.FetchNextBalancesResponse{}, err
}
balance, err := p.client.GetAccountBalances(ctx, resolveAccount(from.Reference))
if err != nil {
return models.FetchNextBalancesResponse{}, err
}
var accountBalances []models.PSPBalance
for _, available := range balance.Available {
if available == nil {
continue
}
timestamp := time.Now()
accountBalances = append(accountBalances, toPSPBalance(from.Reference, timestamp, available))
}
return models.FetchNextBalancesResponse{
Balances: accountBalances,
HasMore: false,
}, nil
}
func toPSPBalance(accountRef string, createdAt time.Time, a *stripe.Amount) models.PSPBalance {
return models.PSPBalance{
AccountReference: accountRef,
Amount: big.NewInt(a.Amount),
Asset: currency.FormatAsset(supportedCurrenciesWithDecimal, string(a.Currency)),
CreatedAt: createdAt,
}
}