fixed operations idempotency

This commit is contained in:
Stephan D
2026-03-04 02:27:12 +01:00
parent f06208348b
commit 8377b6b2af
16 changed files with 353 additions and 80 deletions

View File

@@ -39,6 +39,8 @@ type gatewayClient struct {
logger mlogger.Logger
}
const parentPaymentRefMetadataKey = "parent_payment_ref"
// New dials the Monetix gateway.
func New(ctx context.Context, cfg Config, opts ...grpc.DialOption) (Client, error) {
cfg.setDefaults()
@@ -104,7 +106,7 @@ func (g *gatewayClient) CreateCardPayout(ctx context.Context, req *mntxv1.CardPa
if resp.GetReceipt() != nil && resp.GetReceipt().GetError() != nil {
return nil, connectorError(resp.GetReceipt().GetError())
}
return &mntxv1.CardPayoutResponse{Payout: payoutFromReceipt(req.GetPayoutId(), resp.GetReceipt())}, nil
return &mntxv1.CardPayoutResponse{Payout: payoutFromReceipt(req.GetPayoutId(), req.GetOperationRef(), resp.GetReceipt())}, nil
}
func (g *gatewayClient) CreateCardTokenPayout(ctx context.Context, req *mntxv1.CardTokenPayoutRequest) (*mntxv1.CardTokenPayoutResponse, error) {
@@ -121,7 +123,7 @@ func (g *gatewayClient) CreateCardTokenPayout(ctx context.Context, req *mntxv1.C
if resp.GetReceipt() != nil && resp.GetReceipt().GetError() != nil {
return nil, connectorError(resp.GetReceipt().GetError())
}
return &mntxv1.CardTokenPayoutResponse{Payout: payoutFromReceipt(req.GetPayoutId(), resp.GetReceipt())}, nil
return &mntxv1.CardTokenPayoutResponse{Payout: payoutFromReceipt(req.GetPayoutId(), req.GetOperationRef(), resp.GetReceipt())}, nil
}
func (g *gatewayClient) GetCardPayoutStatus(ctx context.Context, req *mntxv1.GetCardPayoutStatusRequest) (*mntxv1.GetCardPayoutStatusResponse, error) {
@@ -147,10 +149,12 @@ func operationFromCardPayout(req *mntxv1.CardPayoutRequest) (*connectorv1.Operat
}
params := payoutParamsFromCard(req)
money := moneyFromMinor(req.GetAmountMinor(), req.GetCurrency())
operationRef := fallbackNonEmpty(req.GetOperationRef(), req.GetPayoutId())
idempotencyKey := fallbackNonEmpty(req.GetIdempotencyKey(), operationRef)
op := &connectorv1.Operation{
Type: connectorv1.OperationType_PAYOUT,
IdempotencyKey: strings.TrimSpace(req.GetIdempotencyKey()),
OperationRef: strings.TrimSpace(req.GetOperationRef()),
IdempotencyKey: idempotencyKey,
OperationRef: operationRef,
IntentRef: strings.TrimSpace(req.GetIntentRef()),
Money: money,
Params: structFromMap(params),
@@ -165,9 +169,13 @@ func operationFromTokenPayout(req *mntxv1.CardTokenPayoutRequest) (*connectorv1.
}
params := payoutParamsFromToken(req)
money := moneyFromMinor(req.GetAmountMinor(), req.GetCurrency())
operationRef := fallbackNonEmpty(req.GetOperationRef(), req.GetPayoutId())
idempotencyKey := fallbackNonEmpty(req.GetIdempotencyKey(), operationRef)
op := &connectorv1.Operation{
Type: connectorv1.OperationType_PAYOUT,
IdempotencyKey: strings.TrimSpace(req.GetPayoutId()),
IdempotencyKey: idempotencyKey,
OperationRef: operationRef,
IntentRef: strings.TrimSpace(req.GetIntentRef()),
Money: money,
Params: structFromMap(params),
}
@@ -192,8 +200,8 @@ func setOperationRolesFromMetadata(op *connectorv1.Operation, metadata map[strin
}
func payoutParamsFromCard(req *mntxv1.CardPayoutRequest) map[string]interface{} {
metadata := metadataWithParentPaymentRef(req.GetMetadata(), req.GetPayoutId())
params := map[string]interface{}{
"payout_id": strings.TrimSpace(req.GetPayoutId()),
"project_id": req.GetProjectId(),
"customer_id": strings.TrimSpace(req.GetCustomerId()),
"customer_first_name": strings.TrimSpace(req.GetCustomerFirstName()),
@@ -212,15 +220,15 @@ func payoutParamsFromCard(req *mntxv1.CardPayoutRequest) map[string]interface{}
"card_exp_month": req.GetCardExpMonth(),
"card_holder": strings.TrimSpace(req.GetCardHolder()),
}
if len(req.GetMetadata()) > 0 {
params["metadata"] = mapStringToInterface(req.GetMetadata())
if len(metadata) > 0 {
params["metadata"] = mapStringToInterface(metadata)
}
return params
}
func payoutParamsFromToken(req *mntxv1.CardTokenPayoutRequest) map[string]interface{} {
metadata := metadataWithParentPaymentRef(req.GetMetadata(), req.GetPayoutId())
params := map[string]interface{}{
"payout_id": strings.TrimSpace(req.GetPayoutId()),
"project_id": req.GetProjectId(),
"customer_id": strings.TrimSpace(req.GetCustomerId()),
"customer_first_name": strings.TrimSpace(req.GetCustomerFirstName()),
@@ -238,8 +246,8 @@ func payoutParamsFromToken(req *mntxv1.CardTokenPayoutRequest) map[string]interf
"card_holder": strings.TrimSpace(req.GetCardHolder()),
"masked_pan": strings.TrimSpace(req.GetMaskedPan()),
}
if len(req.GetMetadata()) > 0 {
params["metadata"] = mapStringToInterface(req.GetMetadata())
if len(metadata) > 0 {
params["metadata"] = mapStringToInterface(metadata)
}
return params
}
@@ -255,16 +263,53 @@ func moneyFromMinor(amount int64, currency string) *moneyv1.Money {
}
}
func payoutFromReceipt(payoutID string, receipt *connectorv1.OperationReceipt) *mntxv1.CardPayoutState {
state := &mntxv1.CardPayoutState{PayoutId: strings.TrimSpace(payoutID)}
func payoutFromReceipt(payoutID, operationRef string, receipt *connectorv1.OperationReceipt) *mntxv1.CardPayoutState {
state := &mntxv1.CardPayoutState{
PayoutId: fallbackNonEmpty(operationRef, payoutID),
}
if receipt == nil {
return state
}
if opID := strings.TrimSpace(receipt.GetOperationId()); opID != "" {
state.PayoutId = opID
}
state.Status = payoutStatusFromOperation(receipt.GetStatus())
state.ProviderPaymentId = strings.TrimSpace(receipt.GetProviderRef())
return state
}
func fallbackNonEmpty(values ...string) string {
for _, value := range values {
clean := strings.TrimSpace(value)
if clean != "" {
return clean
}
}
return ""
}
func metadataWithParentPaymentRef(source map[string]string, parentPaymentRef string) map[string]string {
parentPaymentRef = strings.TrimSpace(parentPaymentRef)
if len(source) == 0 && parentPaymentRef == "" {
return nil
}
out := map[string]string{}
for key, value := range source {
k := strings.TrimSpace(key)
if k == "" {
continue
}
out[k] = strings.TrimSpace(value)
}
if parentPaymentRef != "" && strings.TrimSpace(out[parentPaymentRefMetadataKey]) == "" {
out[parentPaymentRefMetadataKey] = parentPaymentRef
}
if len(out) == 0 {
return nil
}
return out
}
func payoutFromOperation(op *connectorv1.Operation) *mntxv1.CardPayoutState {
if op == nil {
return nil