181 lines
6.0 KiB
Go
181 lines
6.0 KiB
Go
package quotation
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
chainclient "github.com/tech/sendico/gateway/chain/client"
|
|
"github.com/tech/sendico/pkg/merrors"
|
|
"github.com/tech/sendico/pkg/payments/rail"
|
|
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
|
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
|
|
)
|
|
|
|
type providerSettlementGateway struct {
|
|
client chainclient.Client
|
|
rail string
|
|
network string
|
|
capabilities rail.RailCapabilities
|
|
}
|
|
|
|
func NewProviderSettlementGateway(client chainclient.Client, cfg chainclient.RailGatewayConfig) rail.RailGateway {
|
|
railName := strings.ToUpper(strings.TrimSpace(cfg.Rail))
|
|
if railName == "" {
|
|
railName = "PROVIDER_SETTLEMENT"
|
|
}
|
|
return &providerSettlementGateway{
|
|
client: client,
|
|
rail: railName,
|
|
network: strings.ToUpper(strings.TrimSpace(cfg.Network)),
|
|
capabilities: cfg.Capabilities,
|
|
}
|
|
}
|
|
|
|
func (g *providerSettlementGateway) Rail() string {
|
|
return g.rail
|
|
}
|
|
|
|
func (g *providerSettlementGateway) Network() string {
|
|
return g.network
|
|
}
|
|
|
|
func (g *providerSettlementGateway) Capabilities() rail.RailCapabilities {
|
|
return g.capabilities
|
|
}
|
|
|
|
func (g *providerSettlementGateway) Send(ctx context.Context, req rail.TransferRequest) (rail.RailResult, error) {
|
|
if g.client == nil {
|
|
return rail.RailResult{}, merrors.Internal("provider settlement gateway: client is required")
|
|
}
|
|
idempotencyKey := strings.TrimSpace(req.IdempotencyKey)
|
|
if idempotencyKey == "" {
|
|
return rail.RailResult{}, merrors.InvalidArgument("provider settlement gateway: idempotency_key is required")
|
|
}
|
|
currency := strings.TrimSpace(req.Currency)
|
|
amount := strings.TrimSpace(req.Amount)
|
|
if currency == "" || amount == "" {
|
|
return rail.RailResult{}, merrors.InvalidArgument("provider settlement gateway: amount is required")
|
|
}
|
|
metadata := cloneMetadata(req.Metadata)
|
|
if metadata == nil {
|
|
metadata = map[string]string{}
|
|
}
|
|
if strings.TrimSpace(metadata[providerSettlementMetaPaymentIntentID]) == "" {
|
|
if ref := strings.TrimSpace(req.PaymentRef); ref != "" {
|
|
metadata[providerSettlementMetaPaymentIntentID] = ref
|
|
}
|
|
}
|
|
if strings.TrimSpace(metadata[providerSettlementMetaPaymentIntentID]) == "" {
|
|
return rail.RailResult{}, merrors.InvalidArgument("provider settlement gateway: payment_intent_id is required")
|
|
}
|
|
if strings.TrimSpace(metadata[providerSettlementMetaOutgoingLeg]) == "" && g.rail != "" {
|
|
metadata[providerSettlementMetaOutgoingLeg] = strings.ToLower(strings.TrimSpace(g.rail))
|
|
}
|
|
submitReq := &chainv1.SubmitTransferRequest{
|
|
IdempotencyKey: idempotencyKey,
|
|
OrganizationRef: strings.TrimSpace(req.OrganizationRef),
|
|
SourceWalletRef: strings.TrimSpace(req.FromAccountID),
|
|
Amount: &moneyv1.Money{
|
|
Currency: currency,
|
|
Amount: amount,
|
|
},
|
|
Metadata: metadata,
|
|
PaymentRef: strings.TrimSpace(req.PaymentRef),
|
|
IntentRef: req.IntentRef,
|
|
OperationRef: req.OperationRef,
|
|
}
|
|
if dest := buildProviderSettlementDestination(req); dest != nil {
|
|
submitReq.Destination = dest
|
|
}
|
|
resp, err := g.client.SubmitTransfer(ctx, submitReq)
|
|
if err != nil {
|
|
return rail.RailResult{}, err
|
|
}
|
|
if resp == nil || resp.GetTransfer() == nil {
|
|
return rail.RailResult{}, merrors.Internal("provider settlement gateway: missing transfer response")
|
|
}
|
|
transfer := resp.GetTransfer()
|
|
return rail.RailResult{
|
|
ReferenceID: strings.TrimSpace(transfer.GetTransferRef()),
|
|
Status: providerSettlementStatusFromTransfer(transfer.GetStatus()),
|
|
FinalAmount: railMoneyFromProto(transfer.GetNetAmount()),
|
|
}, nil
|
|
}
|
|
|
|
func (g *providerSettlementGateway) Observe(ctx context.Context, referenceID string) (rail.ObserveResult, error) {
|
|
if g.client == nil {
|
|
return rail.ObserveResult{}, merrors.Internal("provider settlement gateway: client is required")
|
|
}
|
|
ref := strings.TrimSpace(referenceID)
|
|
if ref == "" {
|
|
return rail.ObserveResult{}, merrors.InvalidArgument("provider settlement gateway: reference_id is required")
|
|
}
|
|
resp, err := g.client.GetTransfer(ctx, &chainv1.GetTransferRequest{TransferRef: ref})
|
|
if err != nil {
|
|
return rail.ObserveResult{}, err
|
|
}
|
|
if resp == nil || resp.GetTransfer() == nil {
|
|
return rail.ObserveResult{}, merrors.Internal("provider settlement gateway: missing transfer response")
|
|
}
|
|
transfer := resp.GetTransfer()
|
|
return rail.ObserveResult{
|
|
ReferenceID: ref,
|
|
Status: providerSettlementStatusFromTransfer(transfer.GetStatus()),
|
|
FinalAmount: railMoneyFromProto(transfer.GetNetAmount()),
|
|
}, nil
|
|
}
|
|
|
|
func (g *providerSettlementGateway) Block(ctx context.Context, req rail.BlockRequest) (rail.RailResult, error) {
|
|
return rail.RailResult{}, merrors.NotImplemented("provider settlement gateway: block not supported")
|
|
}
|
|
|
|
func (g *providerSettlementGateway) Release(ctx context.Context, req rail.ReleaseRequest) (rail.RailResult, error) {
|
|
return rail.RailResult{}, merrors.NotImplemented("provider settlement gateway: release not supported")
|
|
}
|
|
|
|
func buildProviderSettlementDestination(req rail.TransferRequest) *chainv1.TransferDestination {
|
|
destRef := strings.TrimSpace(req.ToAccountID)
|
|
memo := strings.TrimSpace(req.DestinationMemo)
|
|
if destRef == "" && memo == "" {
|
|
return nil
|
|
}
|
|
return &chainv1.TransferDestination{
|
|
Destination: &chainv1.TransferDestination_ExternalAddress{ExternalAddress: destRef},
|
|
Memo: memo,
|
|
}
|
|
}
|
|
|
|
func providerSettlementStatusFromTransfer(status chainv1.TransferStatus) rail.TransferStatus {
|
|
switch status {
|
|
|
|
case chainv1.TransferStatus_TRANSFER_SUCCESS:
|
|
return rail.TransferStatusSuccess
|
|
|
|
case chainv1.TransferStatus_TRANSFER_FAILED:
|
|
return rail.TransferStatusFailed
|
|
|
|
case chainv1.TransferStatus_TRANSFER_CANCELLED:
|
|
// our cancellation, not from provider
|
|
return rail.TransferStatusFailed
|
|
|
|
default:
|
|
// CREATED, PROCESSING, WAITING
|
|
return rail.TransferStatusWaiting
|
|
}
|
|
}
|
|
|
|
func railMoneyFromProto(src *moneyv1.Money) *rail.Money {
|
|
if src == nil {
|
|
return nil
|
|
}
|
|
currency := strings.TrimSpace(src.GetCurrency())
|
|
amount := strings.TrimSpace(src.GetAmount())
|
|
if currency == "" || amount == "" {
|
|
return nil
|
|
}
|
|
return &rail.Money{
|
|
Amount: amount,
|
|
Currency: currency,
|
|
}
|
|
}
|