Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0091191d97 |
@@ -8,7 +8,7 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2 v1.41.3
|
github.com/aws/aws-sdk-go-v2 v1.41.3
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.11
|
github.com/aws/aws-sdk-go-v2/config v1.32.11
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.11
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.11
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.0
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.4
|
||||||
github.com/jung-kurt/gofpdf v1.16.2
|
github.com/jung-kurt/gofpdf v1.16.2
|
||||||
github.com/prometheus/client_golang v1.23.2
|
github.com/prometheus/client_golang v1.23.2
|
||||||
github.com/shopspring/decimal v1.4.0
|
github.com/shopspring/decimal v1.4.0
|
||||||
@@ -61,7 +61,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 h1:X1Tow7su
|
|||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19/go.mod h1:/rARO8psX+4sfjUQXp5LLifjUt8DuATZ31WptNJTyQA=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19/go.mod h1:/rARO8psX+4sfjUQXp5LLifjUt8DuATZ31WptNJTyQA=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19 h1:JnQeStZvPHFHeyky/7LbMlyQjUa+jIBj36OlWm0pzIk=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19 h1:JnQeStZvPHFHeyky/7LbMlyQjUa+jIBj36OlWm0pzIk=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19/go.mod h1:HGyasyHvYdFQeJhvDHfH7HXkHh57htcJGKDZ+7z+I24=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19/go.mod h1:HGyasyHvYdFQeJhvDHfH7HXkHh57htcJGKDZ+7z+I24=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.0 h1:zyKY4OxzUImu+DigelJI9o49QQv8CjREs5E1CywjtIA=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.4 h1:4ExZyubQ6LQQVuF2Qp9OsfEvsTdAWh5Gfwf6PgIdLdk=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.0/go.mod h1:NF3JcMGOiARAss1ld3WGORCw71+4ExDD2cbbdKS5PpA=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.4/go.mod h1:NF3JcMGOiARAss1ld3WGORCw71+4ExDD2cbbdKS5PpA=
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.7 h1:Y2cAXlClHsXkkOvWZFXATr34b0hxxloeQu/pAZz2row=
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.7 h1:Y2cAXlClHsXkkOvWZFXATr34b0hxxloeQu/pAZz2row=
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.7/go.mod h1:idzZ7gmDeqeNrSPkdbtMp9qWMgcBwykA7P7Rzh5DXVU=
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.7/go.mod h1:idzZ7gmDeqeNrSPkdbtMp9qWMgcBwykA7P7Rzh5DXVU=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.12 h1:iSsvB9EtQ09YrsmIc44Heqlx5ByGErqhPK1ZQLppias=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.12 h1:iSsvB9EtQ09YrsmIc44Heqlx5ByGErqhPK1ZQLppias=
|
||||||
@@ -229,8 +229,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ package content
|
|||||||
// Issuer details are intentionally centralized to avoid document text drift.
|
// Issuer details are intentionally centralized to avoid document text drift.
|
||||||
const (
|
const (
|
||||||
IssuerLegalName = "SMX Operations Limited"
|
IssuerLegalName = "SMX Operations Limited"
|
||||||
IssuerLegalAddress = "Room 607, 12/F., Block C, Hong Kong Industrial Centre, 489-491 Castle Peak Road, Lai Chi Kok, HongKong"
|
IssuerLegalAddress = "Room 607, 12/F., Block C, Hong Kong Industrial Centre, 489-491 Castle Peak Road, Lai Chi Kok, Hong Kong"
|
||||||
IssuerEmail = "contact@sendico.io"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -75,77 +74,43 @@ var AcceptanceTemplate = AcceptanceTemplateContent{
|
|||||||
|
|
||||||
// OperationDocumentContent contains all static copy for operation documents.
|
// OperationDocumentContent contains all static copy for operation documents.
|
||||||
type OperationDocumentContent struct {
|
type OperationDocumentContent struct {
|
||||||
Title string
|
Title string
|
||||||
Subtitle string
|
Subtitle string
|
||||||
MetaCertificateNumberLabel string
|
MetaDocumentType string
|
||||||
MetaDateLabel string
|
SectionOperation string
|
||||||
SectionParties string
|
SectionFailure string
|
||||||
PartiesIntro string
|
RowOrganization string
|
||||||
RowServiceProvider string
|
RowGatewayService string
|
||||||
RowServiceProviderAddress string
|
RowOperationRef string
|
||||||
RowServiceProviderEmail string
|
RowPaymentRef string
|
||||||
RowClient string
|
RowCode string
|
||||||
RowClientAddress string
|
RowState string
|
||||||
RowClientReference string
|
RowLabel string
|
||||||
SectionSubject string
|
RowStartedAtUTC string
|
||||||
SubjectIntro string
|
RowCompletedAtUTC string
|
||||||
SectionServicePeriod string
|
RowAmount string
|
||||||
RowPeriodFrom string
|
RowFailureCode string
|
||||||
RowPeriodTo string
|
RowFailureReason string
|
||||||
SectionTotalAmount string
|
MissingValuePlaceholder string
|
||||||
RowTotalAmount string
|
|
||||||
SectionClientConfirmation string
|
|
||||||
ConfirmationLine1 string
|
|
||||||
ConfirmationLine2 string
|
|
||||||
ConfirmationLine3 string
|
|
||||||
SectionSignatures string
|
|
||||||
SignatureServiceProviderLine string
|
|
||||||
SignatureClientNamePrefix string
|
|
||||||
SignatureClientTitleLine string
|
|
||||||
SignatureClientLine string
|
|
||||||
SectionOperationStatus string
|
|
||||||
RowOperationStatus string
|
|
||||||
RowOperationCode string
|
|
||||||
RowOperationLabel string
|
|
||||||
RowFailureCode string
|
|
||||||
RowFailureReason string
|
|
||||||
MissingValuePlaceholder string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var OperationDocument = OperationDocumentContent{
|
var OperationDocument = OperationDocumentContent{
|
||||||
Title: "CERTIFICATE OF SERVICES RENDERED",
|
Title: "OPERATION BILLING DOCUMENT",
|
||||||
Subtitle: "Payment operation completion and acceptance statement",
|
Subtitle: "Gateway operation statement",
|
||||||
MetaCertificateNumberLabel: "Certificate No.",
|
MetaDocumentType: "Document Type: Operation",
|
||||||
MetaDateLabel: "Date",
|
SectionOperation: "OPERATION DETAILS",
|
||||||
SectionParties: "PARTIES",
|
SectionFailure: "FAILURE DETAILS",
|
||||||
PartiesIntro: "This Certificate is made between:",
|
RowOrganization: "Organization",
|
||||||
RowServiceProvider: "Service Provider",
|
RowGatewayService: "Gateway Service",
|
||||||
RowServiceProviderAddress: "Service Provider Address",
|
RowOperationRef: "Operation Ref",
|
||||||
RowServiceProviderEmail: "Service Provider Email",
|
RowPaymentRef: "Payment Ref",
|
||||||
RowClient: "Client",
|
RowCode: "Code",
|
||||||
RowClientAddress: "Client Address",
|
RowState: "State",
|
||||||
RowClientReference: "Client Reference",
|
RowLabel: "Label",
|
||||||
SectionSubject: "SUBJECT OF THE CERTIFICATE",
|
RowStartedAtUTC: "Started At (UTC)",
|
||||||
SubjectIntro: "The Service Provider confirms that the following services have been fully rendered to the Client:",
|
RowCompletedAtUTC: "Completed At (UTC)",
|
||||||
SectionServicePeriod: "SERVICE PERIOD",
|
RowAmount: "Amount",
|
||||||
RowPeriodFrom: "From",
|
RowFailureCode: "Failure Code",
|
||||||
RowPeriodTo: "To",
|
RowFailureReason: "Failure Reason",
|
||||||
SectionTotalAmount: "TOTAL AMOUNT",
|
MissingValuePlaceholder: "n/a",
|
||||||
RowTotalAmount: "Amount",
|
|
||||||
SectionClientConfirmation: "CLIENT CONFIRMATION",
|
|
||||||
ConfirmationLine1: "- the services were rendered in full;",
|
|
||||||
ConfirmationLine2: "- the services were rendered properly and within the agreed scope;",
|
|
||||||
ConfirmationLine3: "- the Client has no claims regarding the quality, quantity, or timing of the services rendered.",
|
|
||||||
SectionSignatures: "SIGNATURES",
|
|
||||||
SignatureServiceProviderLine: "Service Provider: Name: SMX Operations Limited | Signature: __________________",
|
|
||||||
SignatureClientNamePrefix: "Client: Name:",
|
|
||||||
SignatureClientTitleLine: "Title: Authorized Representative",
|
|
||||||
SignatureClientLine: "Signature: __________________",
|
|
||||||
SectionOperationStatus: "OPERATION STATUS",
|
|
||||||
RowOperationStatus: "Operation Status",
|
|
||||||
RowOperationCode: "Operation Code",
|
|
||||||
RowOperationLabel: "Operation Label",
|
|
||||||
RowFailureCode: "Failure Code",
|
|
||||||
RowFailureReason: "Failure Reason",
|
|
||||||
MissingValuePlaceholder: "n/a",
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -289,8 +289,6 @@ type operationSnapshot struct {
|
|||||||
GatewayService string
|
GatewayService string
|
||||||
OperationRef string
|
OperationRef string
|
||||||
PaymentRef string
|
PaymentRef string
|
||||||
ClientName string
|
|
||||||
ClientAddress string
|
|
||||||
OperationCode string
|
OperationCode string
|
||||||
OperationLabel string
|
OperationLabel string
|
||||||
OperationState string
|
OperationState string
|
||||||
@@ -308,8 +306,6 @@ func operationSnapshotFromRequest(req *documentsv1.GetOperationDocumentRequest)
|
|||||||
GatewayService: strings.TrimSpace(req.GetGatewayService()),
|
GatewayService: strings.TrimSpace(req.GetGatewayService()),
|
||||||
OperationRef: strings.TrimSpace(req.GetOperationRef()),
|
OperationRef: strings.TrimSpace(req.GetOperationRef()),
|
||||||
PaymentRef: strings.TrimSpace(req.GetPaymentRef()),
|
PaymentRef: strings.TrimSpace(req.GetPaymentRef()),
|
||||||
ClientName: strings.TrimSpace(req.GetClientName()),
|
|
||||||
ClientAddress: strings.TrimSpace(req.GetClientAddress()),
|
|
||||||
OperationCode: strings.TrimSpace(req.GetOperationCode()),
|
OperationCode: strings.TrimSpace(req.GetOperationCode()),
|
||||||
OperationLabel: strings.TrimSpace(req.GetOperationLabel()),
|
OperationLabel: strings.TrimSpace(req.GetOperationLabel()),
|
||||||
OperationState: strings.TrimSpace(req.GetOperationState()),
|
OperationState: strings.TrimSpace(req.GetOperationState()),
|
||||||
@@ -332,6 +328,21 @@ func operationSnapshotFromRequest(req *documentsv1.GetOperationDocumentRequest)
|
|||||||
func buildOperationBlocks(snapshot operationSnapshot) []renderer.Block {
|
func buildOperationBlocks(snapshot operationSnapshot) []renderer.Block {
|
||||||
documentCopy := content.OperationDocument
|
documentCopy := content.OperationDocument
|
||||||
|
|
||||||
|
rows := [][]string{
|
||||||
|
{documentCopy.RowOrganization, snapshot.OrganizationRef},
|
||||||
|
{documentCopy.RowGatewayService, snapshot.GatewayService},
|
||||||
|
{documentCopy.RowOperationRef, snapshot.OperationRef},
|
||||||
|
{documentCopy.RowPaymentRef, safeValue(snapshot.PaymentRef)},
|
||||||
|
{documentCopy.RowCode, safeValue(snapshot.OperationCode)},
|
||||||
|
{documentCopy.RowState, safeValue(snapshot.OperationState)},
|
||||||
|
{documentCopy.RowLabel, safeValue(snapshot.OperationLabel)},
|
||||||
|
{documentCopy.RowStartedAtUTC, formatSnapshotTime(snapshot.StartedAt)},
|
||||||
|
{documentCopy.RowCompletedAtUTC, formatSnapshotTime(snapshot.CompletedAt)},
|
||||||
|
}
|
||||||
|
if snapshot.Amount != "" || snapshot.Currency != "" {
|
||||||
|
rows = append(rows, []string{documentCopy.RowAmount, strings.TrimSpace(strings.TrimSpace(snapshot.Amount) + " " + strings.TrimSpace(snapshot.Currency))})
|
||||||
|
}
|
||||||
|
|
||||||
blocks := []renderer.Block{
|
blocks := []renderer.Block{
|
||||||
{
|
{
|
||||||
Tag: renderer.TagTitle,
|
Tag: renderer.TagTitle,
|
||||||
@@ -344,115 +355,30 @@ func buildOperationBlocks(snapshot operationSnapshot) []renderer.Block {
|
|||||||
{
|
{
|
||||||
Tag: renderer.TagMeta,
|
Tag: renderer.TagMeta,
|
||||||
Lines: []string{
|
Lines: []string{
|
||||||
fmt.Sprintf("%s: %s", documentCopy.MetaCertificateNumberLabel, certificateNumber(snapshot)),
|
documentCopy.MetaDocumentType,
|
||||||
fmt.Sprintf("%s: %s", documentCopy.MetaDateLabel, formatCertificateDate(certificateDate(snapshot))),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Tag: renderer.TagSection,
|
Tag: renderer.TagSection,
|
||||||
Lines: []string{documentCopy.SectionParties},
|
Lines: []string{documentCopy.SectionOperation},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Tag: renderer.TagText,
|
Tag: renderer.TagKV,
|
||||||
Lines: []string{documentCopy.PartiesIntro},
|
Rows: rows,
|
||||||
},
|
|
||||||
{
|
|
||||||
Tag: renderer.TagKV,
|
|
||||||
Rows: [][]string{
|
|
||||||
{documentCopy.RowServiceProvider, content.IssuerLegalName},
|
|
||||||
{documentCopy.RowServiceProviderAddress, content.IssuerLegalAddress},
|
|
||||||
{documentCopy.RowServiceProviderEmail, content.IssuerEmail},
|
|
||||||
{documentCopy.RowClient, certificateClientName(snapshot)},
|
|
||||||
{documentCopy.RowClientAddress, certificateClientAddress(snapshot)},
|
|
||||||
{documentCopy.RowClientReference, safeValue(snapshot.PaymentRef)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Tag: renderer.TagSection,
|
|
||||||
Lines: []string{documentCopy.SectionSubject},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Tag: renderer.TagText,
|
|
||||||
Lines: []string{
|
|
||||||
documentCopy.SubjectIntro,
|
|
||||||
"",
|
|
||||||
"- Payment execution and orchestration services for payment reference " + safeValue(snapshot.PaymentRef) + ".",
|
|
||||||
"- Gateway service: " + safeValue(snapshot.GatewayService) + ".",
|
|
||||||
"- Operation reference: " + safeValue(snapshot.OperationRef) + ".",
|
|
||||||
"- Operation descriptor: " + operationDescriptor(snapshot) + ".",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Tag: renderer.TagSection,
|
|
||||||
Lines: []string{documentCopy.SectionServicePeriod},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Tag: renderer.TagKV,
|
|
||||||
Rows: [][]string{
|
|
||||||
{documentCopy.RowPeriodFrom, formatSnapshotTime(snapshot.StartedAt)},
|
|
||||||
{documentCopy.RowPeriodTo, formatSnapshotTime(snapshot.CompletedAt)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Tag: renderer.TagSection,
|
|
||||||
Lines: []string{documentCopy.SectionTotalAmount},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Tag: renderer.TagKV,
|
|
||||||
Rows: [][]string{
|
|
||||||
{documentCopy.RowTotalAmount, operationAmount(snapshot)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Tag: renderer.TagSection,
|
|
||||||
Lines: []string{documentCopy.SectionClientConfirmation},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Tag: renderer.TagText,
|
|
||||||
Lines: []string{
|
|
||||||
documentCopy.ConfirmationLine1,
|
|
||||||
documentCopy.ConfirmationLine2,
|
|
||||||
documentCopy.ConfirmationLine3,
|
|
||||||
"",
|
|
||||||
"This Certificate serves as confirmation of the completion and acceptance of the services.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Tag: renderer.TagSection,
|
|
||||||
Lines: []string{documentCopy.SectionSignatures},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Tag: renderer.TagSign,
|
|
||||||
Lines: []string{
|
|
||||||
documentCopy.SignatureServiceProviderLine,
|
|
||||||
"",
|
|
||||||
documentCopy.SignatureClientNamePrefix + " " + certificateClientName(snapshot),
|
|
||||||
documentCopy.SignatureClientTitleLine,
|
|
||||||
documentCopy.SignatureClientLine,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Tag: renderer.TagSection,
|
|
||||||
Lines: []string{documentCopy.SectionOperationStatus},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Tag: renderer.TagKV,
|
|
||||||
Rows: [][]string{
|
|
||||||
{documentCopy.RowOperationStatus, safeValue(snapshot.OperationState)},
|
|
||||||
{documentCopy.RowOperationCode, safeValue(snapshot.OperationCode)},
|
|
||||||
{documentCopy.RowOperationLabel, safeValue(snapshot.OperationLabel)},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if snapshot.FailureCode != "" || snapshot.FailureReason != "" {
|
if snapshot.FailureCode != "" || snapshot.FailureReason != "" {
|
||||||
blocks = append(blocks, renderer.Block{
|
blocks = append(blocks,
|
||||||
Tag: renderer.TagKV,
|
renderer.Block{Tag: renderer.TagSection, Lines: []string{documentCopy.SectionFailure}},
|
||||||
Rows: [][]string{
|
renderer.Block{
|
||||||
{documentCopy.RowFailureCode, safeValue(snapshot.FailureCode)},
|
Tag: renderer.TagKV,
|
||||||
{documentCopy.RowFailureReason, safeValue(snapshot.FailureReason)},
|
Rows: [][]string{
|
||||||
|
{documentCopy.RowFailureCode, safeValue(snapshot.FailureCode)},
|
||||||
|
{documentCopy.RowFailureReason, safeValue(snapshot.FailureReason)},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return blocks
|
return blocks
|
||||||
@@ -466,80 +392,6 @@ func formatSnapshotTime(value time.Time) string {
|
|||||||
return value.UTC().Format(time.RFC3339)
|
return value.UTC().Format(time.RFC3339)
|
||||||
}
|
}
|
||||||
|
|
||||||
func certificateNumber(snapshot operationSnapshot) string {
|
|
||||||
if paymentRef := strings.TrimSpace(snapshot.PaymentRef); paymentRef != "" {
|
|
||||||
return paymentRef
|
|
||||||
}
|
|
||||||
|
|
||||||
if operationRef := strings.TrimSpace(snapshot.OperationRef); operationRef != "" {
|
|
||||||
return operationRef
|
|
||||||
}
|
|
||||||
|
|
||||||
return content.OperationDocument.MissingValuePlaceholder
|
|
||||||
}
|
|
||||||
|
|
||||||
func certificateDate(snapshot operationSnapshot) time.Time {
|
|
||||||
if !snapshot.CompletedAt.IsZero() {
|
|
||||||
return snapshot.CompletedAt.UTC()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !snapshot.StartedAt.IsZero() {
|
|
||||||
return snapshot.StartedAt.UTC()
|
|
||||||
}
|
|
||||||
|
|
||||||
return time.Now().UTC()
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatCertificateDate(value time.Time) string {
|
|
||||||
if value.IsZero() {
|
|
||||||
return content.OperationDocument.MissingValuePlaceholder
|
|
||||||
}
|
|
||||||
|
|
||||||
return value.UTC().Format("January 2, 2006")
|
|
||||||
}
|
|
||||||
|
|
||||||
func operationAmount(snapshot operationSnapshot) string {
|
|
||||||
amount := strings.TrimSpace(snapshot.Amount)
|
|
||||||
currency := strings.TrimSpace(snapshot.Currency)
|
|
||||||
if amount == "" && currency == "" {
|
|
||||||
return content.OperationDocument.MissingValuePlaceholder
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimSpace(amount + " " + currency)
|
|
||||||
}
|
|
||||||
|
|
||||||
func operationDescriptor(snapshot operationSnapshot) string {
|
|
||||||
label := strings.TrimSpace(snapshot.OperationLabel)
|
|
||||||
code := strings.TrimSpace(snapshot.OperationCode)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case label != "" && code != "":
|
|
||||||
return fmt.Sprintf("%s (%s)", label, code)
|
|
||||||
case label != "":
|
|
||||||
return label
|
|
||||||
case code != "":
|
|
||||||
return code
|
|
||||||
default:
|
|
||||||
return content.OperationDocument.MissingValuePlaceholder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func certificateClientName(snapshot operationSnapshot) string {
|
|
||||||
if name := strings.TrimSpace(snapshot.ClientName); name != "" {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
return "John Doe"
|
|
||||||
}
|
|
||||||
|
|
||||||
func certificateClientAddress(snapshot operationSnapshot) string {
|
|
||||||
if address := strings.TrimSpace(snapshot.ClientAddress); address != "" {
|
|
||||||
return address
|
|
||||||
}
|
|
||||||
|
|
||||||
return content.OperationDocument.MissingValuePlaceholder
|
|
||||||
}
|
|
||||||
|
|
||||||
func safeValue(value string) string {
|
func safeValue(value string) string {
|
||||||
trimmed := strings.TrimSpace(value)
|
trimmed := strings.TrimSpace(value)
|
||||||
if trimmed == "" {
|
if trimmed == "" {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package documents
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -192,92 +191,3 @@ func TestGetOperationDocument_RequiresOperationRef(t *testing.T) {
|
|||||||
t.Fatalf("expected InvalidArgument, got=%v err=%v", status.Code(err), err)
|
t.Fatalf("expected InvalidArgument, got=%v err=%v", status.Code(err), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildOperationBlocks_CertificateIncludesPaymentData(t *testing.T) {
|
|
||||||
snapshot := operationSnapshot{
|
|
||||||
OrganizationRef: "org-1",
|
|
||||||
GatewayService: "chain_gateway",
|
|
||||||
OperationRef: "op-123",
|
|
||||||
PaymentRef: "pay-123",
|
|
||||||
ClientName: "Jane Customer",
|
|
||||||
ClientAddress: "Main Street 1, City",
|
|
||||||
OperationCode: "transfer",
|
|
||||||
OperationLabel: "Outbound transfer",
|
|
||||||
OperationState: "completed",
|
|
||||||
Amount: "100.50",
|
|
||||||
Currency: "USDT",
|
|
||||||
StartedAt: time.Date(2026, 3, 1, 10, 0, 0, 0, time.UTC),
|
|
||||||
CompletedAt: time.Date(2026, 3, 2, 12, 0, 0, 0, time.UTC),
|
|
||||||
}
|
|
||||||
|
|
||||||
blocks := buildOperationBlocks(snapshot)
|
|
||||||
if len(blocks) == 0 {
|
|
||||||
t.Fatalf("expected blocks")
|
|
||||||
}
|
|
||||||
|
|
||||||
if got := blocks[0].Lines[0]; got != content.OperationDocument.Title {
|
|
||||||
t.Fatalf("title mismatch: got=%q want=%q", got, content.OperationDocument.Title)
|
|
||||||
}
|
|
||||||
|
|
||||||
meta := findTaggedBlock(blocks, renderer.TagMeta)
|
|
||||||
if meta == nil {
|
|
||||||
t.Fatalf("expected meta block")
|
|
||||||
}
|
|
||||||
|
|
||||||
metaText := strings.Join(meta.Lines, "\n")
|
|
||||||
if !strings.Contains(metaText, "Certificate No.: pay-123") {
|
|
||||||
t.Fatalf("meta should include certificate number, got=%q", metaText)
|
|
||||||
}
|
|
||||||
if !strings.Contains(metaText, "Date: March 2, 2026") {
|
|
||||||
t.Fatalf("meta should include certificate date, got=%q", metaText)
|
|
||||||
}
|
|
||||||
|
|
||||||
amountFound := false
|
|
||||||
clientFound := false
|
|
||||||
for _, block := range blocks {
|
|
||||||
if block.Tag != renderer.TagKV {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, row := range block.Rows {
|
|
||||||
if len(row) >= 2 && row[0] == content.OperationDocument.RowTotalAmount && row[1] == "100.50 USDT" {
|
|
||||||
amountFound = true
|
|
||||||
}
|
|
||||||
if len(row) >= 2 && row[0] == content.OperationDocument.RowClient && row[1] == "Jane Customer" {
|
|
||||||
clientFound = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !amountFound {
|
|
||||||
t.Fatalf("expected total amount row with payment amount")
|
|
||||||
}
|
|
||||||
if !clientFound {
|
|
||||||
t.Fatalf("expected client row with customer name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCertificateNumber_FallsBackToOperationRef(t *testing.T) {
|
|
||||||
got := certificateNumber(operationSnapshot{
|
|
||||||
OperationRef: "op-777",
|
|
||||||
})
|
|
||||||
|
|
||||||
if got != "op-777" {
|
|
||||||
t.Fatalf("certificateNumber fallback mismatch: got=%q want=%q", got, "op-777")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCertificateClientName_Fallback(t *testing.T) {
|
|
||||||
if got := certificateClientName(operationSnapshot{}); got != "John Doe" {
|
|
||||||
t.Fatalf("certificateClientName fallback mismatch: got=%q want=%q", got, "John Doe")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func findTaggedBlock(blocks []renderer.Block, tag renderer.Tag) *renderer.Block {
|
|
||||||
for i := range blocks {
|
|
||||||
if blocks[i].Tag == tag {
|
|
||||||
return &blocks[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -179,8 +179,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -179,8 +179,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2 v1.41.3
|
github.com/aws/aws-sdk-go-v2 v1.41.3
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.11
|
github.com/aws/aws-sdk-go-v2/config v1.32.11
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.11
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.11
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.0
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.4
|
||||||
github.com/go-chi/chi/v5 v5.2.5
|
github.com/go-chi/chi/v5 v5.2.5
|
||||||
github.com/go-chi/cors v1.2.2
|
github.com/go-chi/cors v1.2.2
|
||||||
github.com/go-chi/jwtauth/v5 v5.4.0
|
github.com/go-chi/jwtauth/v5 v5.4.0
|
||||||
@@ -37,7 +37,7 @@ require (
|
|||||||
github.com/testcontainers/testcontainers-go/modules/mongodb v0.33.0
|
github.com/testcontainers/testcontainers-go/modules/mongodb v0.33.0
|
||||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||||
go.uber.org/zap v1.27.1
|
go.uber.org/zap v1.27.1
|
||||||
golang.org/x/net v0.52.0
|
golang.org/x/net v0.51.0
|
||||||
google.golang.org/grpc v1.79.2
|
google.golang.org/grpc v1.79.2
|
||||||
google.golang.org/protobuf v1.36.11
|
google.golang.org/protobuf v1.36.11
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
@@ -88,7 +88,7 @@ require (
|
|||||||
github.com/go-logr/logr v1.4.3 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.6 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 h1:X1Tow7su
|
|||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19/go.mod h1:/rARO8psX+4sfjUQXp5LLifjUt8DuATZ31WptNJTyQA=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19/go.mod h1:/rARO8psX+4sfjUQXp5LLifjUt8DuATZ31WptNJTyQA=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19 h1:JnQeStZvPHFHeyky/7LbMlyQjUa+jIBj36OlWm0pzIk=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19 h1:JnQeStZvPHFHeyky/7LbMlyQjUa+jIBj36OlWm0pzIk=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19/go.mod h1:HGyasyHvYdFQeJhvDHfH7HXkHh57htcJGKDZ+7z+I24=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19/go.mod h1:HGyasyHvYdFQeJhvDHfH7HXkHh57htcJGKDZ+7z+I24=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.0 h1:zyKY4OxzUImu+DigelJI9o49QQv8CjREs5E1CywjtIA=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.4 h1:4ExZyubQ6LQQVuF2Qp9OsfEvsTdAWh5Gfwf6PgIdLdk=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.0/go.mod h1:NF3JcMGOiARAss1ld3WGORCw71+4ExDD2cbbdKS5PpA=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.4/go.mod h1:NF3JcMGOiARAss1ld3WGORCw71+4ExDD2cbbdKS5PpA=
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.7 h1:Y2cAXlClHsXkkOvWZFXATr34b0hxxloeQu/pAZz2row=
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.7 h1:Y2cAXlClHsXkkOvWZFXATr34b0hxxloeQu/pAZz2row=
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.7/go.mod h1:idzZ7gmDeqeNrSPkdbtMp9qWMgcBwykA7P7Rzh5DXVU=
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.7/go.mod h1:idzZ7gmDeqeNrSPkdbtMp9qWMgcBwykA7P7Rzh5DXVU=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.12 h1:iSsvB9EtQ09YrsmIc44Heqlx5ByGErqhPK1ZQLppias=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.12 h1:iSsvB9EtQ09YrsmIc44Heqlx5ByGErqhPK1ZQLppias=
|
||||||
@@ -112,8 +112,8 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
|||||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||||
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
||||||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||||
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
@@ -346,8 +346,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ import (
|
|||||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||||
documentsv1 "github.com/tech/sendico/pkg/proto/billing/documents/v1"
|
documentsv1 "github.com/tech/sendico/pkg/proto/billing/documents/v1"
|
||||||
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
||||||
endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1"
|
|
||||||
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
|
||||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
@@ -26,6 +24,7 @@ import (
|
|||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -62,10 +61,6 @@ func (a *PaymentAPI) getOperationDocument(r *http.Request, account *model.Accoun
|
|||||||
if operationRef == "" {
|
if operationRef == "" {
|
||||||
return response.BadRequest(a.logger, a.Name(), "missing_parameter", "operation_ref is required")
|
return response.BadRequest(a.logger, a.Name(), "missing_parameter", "operation_ref is required")
|
||||||
}
|
}
|
||||||
paymentRef := strings.TrimSpace(query.Get("payment_ref"))
|
|
||||||
if paymentRef == "" {
|
|
||||||
return response.BadRequest(a.logger, a.Name(), "missing_parameter", "payment_ref is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
service, gateway, h := a.resolveOperationDocumentDeps(r.Context(), gatewayService)
|
service, gateway, h := a.resolveOperationDocumentDeps(r.Context(), gatewayService)
|
||||||
if h != nil {
|
if h != nil {
|
||||||
@@ -78,18 +73,7 @@ func (a *PaymentAPI) getOperationDocument(r *http.Request, account *model.Accoun
|
|||||||
return documentErrorResponse(a.logger, a.Name(), err)
|
return documentErrorResponse(a.logger, a.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req := operationDocumentRequest(orgRef.Hex(), gatewayService, operationRef, paymentRef, op)
|
req := operationDocumentRequest(orgRef.Hex(), gatewayService, operationRef, op)
|
||||||
if payment, paymentErr := a.fetchPayment(r.Context(), orgRef.Hex(), paymentRef); paymentErr != nil {
|
|
||||||
a.logger.Warn(
|
|
||||||
"Failed to fetch payment snapshot for operation document",
|
|
||||||
zap.Error(paymentErr),
|
|
||||||
mzap.ObjRef("organization_ref", orgRef),
|
|
||||||
zap.String("payment_ref", paymentRef),
|
|
||||||
zap.String("operation_ref", operationRef),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
enrichOperationDocumentRequestFromPayment(req, payment)
|
|
||||||
}
|
|
||||||
|
|
||||||
docResp, err := a.fetchOperationDocument(r.Context(), service.InvokeURI, req)
|
docResp, err := a.fetchOperationDocument(r.Context(), service.InvokeURI, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -279,28 +263,6 @@ func (a *PaymentAPI) fetchGatewayOperation(ctx context.Context, invokeURI, opera
|
|||||||
return op, nil
|
return op, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *PaymentAPI) fetchPayment(ctx context.Context, organizationRef, paymentRef string) (*orchestrationv2.Payment, error) {
|
|
||||||
if a.execution == nil {
|
|
||||||
return nil, merrors.Internal("payment execution client is not configured")
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := a.execution.GetPayment(ctx, &orchestrationv2.GetPaymentRequest{
|
|
||||||
Meta: requestMeta(organizationRef, ""),
|
|
||||||
PaymentRef: strings.TrimSpace(paymentRef),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if resp == nil {
|
|
||||||
return nil, merrors.NoData("payment orchestrator returned empty response")
|
|
||||||
}
|
|
||||||
if resp.GetPayment() == nil {
|
|
||||||
return nil, merrors.NoData("payment orchestrator returned empty payment")
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp.GetPayment(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func findGatewayForService(gateways []discovery.GatewaySummary, gatewayService mservice.Type) *discovery.GatewaySummary {
|
func findGatewayForService(gateways []discovery.GatewaySummary, gatewayService mservice.Type) *discovery.GatewaySummary {
|
||||||
candidates := make([]discovery.GatewaySummary, 0, len(gateways))
|
candidates := make([]discovery.GatewaySummary, 0, len(gateways))
|
||||||
for _, gw := range gateways {
|
for _, gw := range gateways {
|
||||||
@@ -351,14 +313,13 @@ func findGatewayForService(gateways []discovery.GatewaySummary, gatewayService m
|
|||||||
return &best
|
return &best
|
||||||
}
|
}
|
||||||
|
|
||||||
func operationDocumentRequest(organizationRef string, gatewayService mservice.Type, requestedOperationRef string, paymentRef string, op *connectorv1.Operation) *documentsv1.GetOperationDocumentRequest {
|
func operationDocumentRequest(organizationRef string, gatewayService mservice.Type, requestedOperationRef string, op *connectorv1.Operation) *documentsv1.GetOperationDocumentRequest {
|
||||||
req := &documentsv1.GetOperationDocumentRequest{
|
req := &documentsv1.GetOperationDocumentRequest{
|
||||||
OrganizationRef: strings.TrimSpace(organizationRef),
|
OrganizationRef: strings.TrimSpace(organizationRef),
|
||||||
GatewayService: gatewayService,
|
GatewayService: gatewayService,
|
||||||
OperationRef: firstNonEmpty(strings.TrimSpace(op.GetOperationRef()), strings.TrimSpace(requestedOperationRef)),
|
OperationRef: firstNonEmpty(strings.TrimSpace(op.GetOperationRef()), strings.TrimSpace(requestedOperationRef)),
|
||||||
PaymentRef: strings.TrimSpace(paymentRef),
|
|
||||||
OperationCode: strings.TrimSpace(op.GetType().String()),
|
OperationCode: strings.TrimSpace(op.GetType().String()),
|
||||||
OperationLabel: strings.TrimSpace(op.GetType().String()),
|
OperationLabel: operationLabel(op.GetType()),
|
||||||
OperationState: strings.TrimSpace(op.GetStatus().String()),
|
OperationState: strings.TrimSpace(op.GetStatus().String()),
|
||||||
Amount: strings.TrimSpace(op.GetMoney().GetAmount()),
|
Amount: strings.TrimSpace(op.GetMoney().GetAmount()),
|
||||||
Currency: strings.TrimSpace(op.GetMoney().GetCurrency()),
|
Currency: strings.TrimSpace(op.GetMoney().GetCurrency()),
|
||||||
@@ -371,96 +332,65 @@ func operationDocumentRequest(organizationRef string, gatewayService mservice.Ty
|
|||||||
req.CompletedAtUnixMs = ts.AsTime().UnixMilli()
|
req.CompletedAtUnixMs = ts.AsTime().UnixMilli()
|
||||||
}
|
}
|
||||||
|
|
||||||
if isFailedOperationStatus(op.GetStatus()) {
|
req.PaymentRef = operationParamValue(op.GetParams(), "payment_ref", "parent_payment_ref", "paymentRef", "parentPaymentRef")
|
||||||
req.FailureCode = strings.TrimSpace(op.GetStatus().String())
|
req.FailureCode = firstNonEmpty(
|
||||||
}
|
operationParamValue(op.GetParams(), "failure_code", "provider_code", "error_code"),
|
||||||
|
failureCodeFromStatus(op.GetStatus()),
|
||||||
|
)
|
||||||
|
req.FailureReason = operationParamValue(op.GetParams(), "failure_reason", "provider_message", "error", "message")
|
||||||
|
|
||||||
return req
|
return req
|
||||||
}
|
}
|
||||||
|
|
||||||
func isFailedOperationStatus(status connectorv1.OperationStatus) bool {
|
func operationLabel(opType connectorv1.OperationType) string {
|
||||||
return status == connectorv1.OperationStatus_OPERATION_FAILED || status == connectorv1.OperationStatus_OPERATION_CANCELLED
|
switch opType {
|
||||||
|
case connectorv1.OperationType_CREDIT:
|
||||||
|
return "Credit"
|
||||||
|
case connectorv1.OperationType_DEBIT:
|
||||||
|
return "Debit"
|
||||||
|
case connectorv1.OperationType_TRANSFER:
|
||||||
|
return "Transfer"
|
||||||
|
case connectorv1.OperationType_PAYOUT:
|
||||||
|
return "Payout"
|
||||||
|
case connectorv1.OperationType_FEE_ESTIMATE:
|
||||||
|
return "Fee Estimate"
|
||||||
|
case connectorv1.OperationType_FX:
|
||||||
|
return "FX"
|
||||||
|
case connectorv1.OperationType_GAS_TOPUP:
|
||||||
|
return "Gas Top Up"
|
||||||
|
default:
|
||||||
|
return strings.TrimSpace(opType.String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func enrichOperationDocumentRequestFromPayment(req *documentsv1.GetOperationDocumentRequest, payment *orchestrationv2.Payment) {
|
func failureCodeFromStatus(status connectorv1.OperationStatus) string {
|
||||||
if req == nil || payment == nil {
|
switch status {
|
||||||
return
|
case connectorv1.OperationStatus_OPERATION_FAILED, connectorv1.OperationStatus_OPERATION_CANCELLED:
|
||||||
}
|
return strings.TrimSpace(status.String())
|
||||||
|
|
||||||
req.PaymentRef = firstNonEmpty(strings.TrimSpace(req.GetPaymentRef()), strings.TrimSpace(payment.GetPaymentRef()))
|
|
||||||
req.ClientName = firstNonEmpty(strings.TrimSpace(req.GetClientName()), paymentClientName(payment))
|
|
||||||
}
|
|
||||||
|
|
||||||
func paymentClientName(payment *orchestrationv2.Payment) string {
|
|
||||||
if payment == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
intent := payment.GetIntentSnapshot()
|
|
||||||
if intent == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if customerDescription := strings.TrimSpace(intent.GetComment()); customerDescription != "" {
|
|
||||||
return customerDescription
|
|
||||||
}
|
|
||||||
|
|
||||||
return firstNonEmpty(
|
|
||||||
paymentEndpointClientName(intent.GetDestination()),
|
|
||||||
paymentEndpointClientName(intent.GetSource()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func paymentEndpointClientName(endpoint *endpointv1.PaymentEndpoint) string {
|
|
||||||
if endpoint == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
method := endpoint.GetPaymentMethod()
|
|
||||||
if method == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
switch method.GetType() {
|
|
||||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD:
|
|
||||||
type cardMethodData struct {
|
|
||||||
FirstName string `bson:"firstName"`
|
|
||||||
LastName string `bson:"lastName"`
|
|
||||||
}
|
|
||||||
var payload cardMethodData
|
|
||||||
if err := bson.Unmarshal(method.GetData(), &payload); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(strings.Join([]string{
|
|
||||||
strings.TrimSpace(payload.FirstName),
|
|
||||||
strings.TrimSpace(payload.LastName),
|
|
||||||
}, " "))
|
|
||||||
|
|
||||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_BANK_ACCOUNT:
|
|
||||||
type bankMethodData struct {
|
|
||||||
RecipientName string `bson:"recipientName"`
|
|
||||||
}
|
|
||||||
var payload bankMethodData
|
|
||||||
if err := bson.Unmarshal(method.GetData(), &payload); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(payload.RecipientName)
|
|
||||||
|
|
||||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_IBAN:
|
|
||||||
type ibanMethodData struct {
|
|
||||||
AccountHolder string `bson:"accountHolder"`
|
|
||||||
}
|
|
||||||
var payload ibanMethodData
|
|
||||||
if err := bson.Unmarshal(method.GetData(), &payload); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(payload.AccountHolder)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func operationParamValue(params *structpb.Struct, keys ...string) string {
|
||||||
|
if params == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
values := params.AsMap()
|
||||||
|
for _, key := range keys {
|
||||||
|
raw, ok := values[key]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if text := strings.TrimSpace(fmt.Sprint(raw)); text != "" && text != "<nil>" {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func findDocumentsService(services []discovery.ServiceSummary) *discovery.ServiceSummary {
|
func findDocumentsService(services []discovery.ServiceSummary) *discovery.ServiceSummary {
|
||||||
for _, svc := range services {
|
for _, svc := range services {
|
||||||
if !strings.EqualFold(svc.Service, documentsServiceName) {
|
if !strings.EqualFold(svc.Service, documentsServiceName) {
|
||||||
|
|||||||
@@ -1,120 +0,0 @@
|
|||||||
package paymentapiimp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/tech/sendico/pkg/mservice"
|
|
||||||
documentsv1 "github.com/tech/sendico/pkg/proto/billing/documents/v1"
|
|
||||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
|
||||||
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
|
||||||
endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1"
|
|
||||||
orchestrationv2 "github.com/tech/sendico/pkg/proto/payments/orchestration/v2"
|
|
||||||
quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
|
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestOperationDocumentRequest_UsesStructuredOperationFieldsOnly(t *testing.T) {
|
|
||||||
op := &connectorv1.Operation{
|
|
||||||
OperationRef: "pay-123:hop_1",
|
|
||||||
Type: connectorv1.OperationType_TRANSFER,
|
|
||||||
Status: connectorv1.OperationStatus_OPERATION_SUCCESS,
|
|
||||||
Money: &moneyv1.Money{
|
|
||||||
Amount: "100.50",
|
|
||||||
Currency: "USDT",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
req := operationDocumentRequest("org-1", mservice.ChainGateway, "requested-op", "pay-123", op)
|
|
||||||
if req == nil {
|
|
||||||
t.Fatalf("expected request")
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := req.GetPaymentRef(), "pay-123"; got != want {
|
|
||||||
t.Fatalf("payment_ref mismatch: got=%q want=%q", got, want)
|
|
||||||
}
|
|
||||||
if got := req.GetClientName(); got != "" {
|
|
||||||
t.Fatalf("expected empty client_name from operation-only request, got=%q", got)
|
|
||||||
}
|
|
||||||
if got := req.GetClientAddress(); got != "" {
|
|
||||||
t.Fatalf("expected empty client_address from operation-only request, got=%q", got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOperationDocumentRequest_FailureCodeFromStructuredStatus(t *testing.T) {
|
|
||||||
req := operationDocumentRequest("org-1", mservice.ChainGateway, "op", "pay-123", &connectorv1.Operation{
|
|
||||||
OperationRef: "pay-123:hop_1",
|
|
||||||
Type: connectorv1.OperationType_TRANSFER,
|
|
||||||
Status: connectorv1.OperationStatus_OPERATION_FAILED,
|
|
||||||
})
|
|
||||||
if req == nil {
|
|
||||||
t.Fatalf("expected request")
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := req.GetFailureCode(), "OPERATION_FAILED"; got != want {
|
|
||||||
t.Fatalf("failure_code mismatch: got=%q want=%q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPaymentClientName_FromIntentComment(t *testing.T) {
|
|
||||||
payment := &orchestrationv2.Payment{
|
|
||||||
IntentSnapshot: "ationv2.QuoteIntent{
|
|
||||||
Comment: "Jane Customer",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := paymentClientName(payment), "Jane Customer"; got != want {
|
|
||||||
t.Fatalf("paymentClientName mismatch: got=%q want=%q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPaymentClientName_FromStructuredCardEndpoint(t *testing.T) {
|
|
||||||
raw, err := bson.Marshal(map[string]string{
|
|
||||||
"firstName": "Jane",
|
|
||||||
"lastName": "Doe",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Marshal: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
payment := &orchestrationv2.Payment{
|
|
||||||
IntentSnapshot: "ationv2.QuoteIntent{
|
|
||||||
Destination: &endpointv1.PaymentEndpoint{
|
|
||||||
Source: &endpointv1.PaymentEndpoint_PaymentMethod{
|
|
||||||
PaymentMethod: &endpointv1.PaymentMethod{
|
|
||||||
Type: endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD,
|
|
||||||
Data: raw,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if got, want := paymentClientName(payment), "Jane Doe"; got != want {
|
|
||||||
t.Fatalf("paymentClientName mismatch: got=%q want=%q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEnrichOperationDocumentRequestFromPayment_SetsClientName(t *testing.T) {
|
|
||||||
req := &documentsv1.GetOperationDocumentRequest{
|
|
||||||
OperationRef: "pay-123:hop_1",
|
|
||||||
}
|
|
||||||
|
|
||||||
payment := &orchestrationv2.Payment{
|
|
||||||
PaymentRef: "pay-123",
|
|
||||||
IntentSnapshot: "ationv2.QuoteIntent{
|
|
||||||
Comment: "Client Name",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
enrichOperationDocumentRequestFromPayment(req, payment)
|
|
||||||
if got, want := req.GetPaymentRef(), "pay-123"; got != want {
|
|
||||||
t.Fatalf("payment_ref mismatch: got=%q want=%q", got, want)
|
|
||||||
}
|
|
||||||
if got, want := req.GetClientName(), "Client Name"; got != want {
|
|
||||||
t.Fatalf("client_name mismatch: got=%q want=%q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOperationDocumentRequest_Compatibility(t *testing.T) {
|
|
||||||
var _ *documentsv1.GetOperationDocumentRequest = operationDocumentRequest("org-1", mservice.ChainGateway, "op", "pay-1", &connectorv1.Operation{})
|
|
||||||
}
|
|
||||||
@@ -165,10 +165,6 @@ func (*fakeExecutionClientForBatch) ListPayments(context.Context, *orchestration
|
|||||||
return &orchestrationv2.ListPaymentsResponse{}, nil
|
return &orchestrationv2.ListPaymentsResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*fakeExecutionClientForBatch) GetPayment(context.Context, *orchestrationv2.GetPaymentRequest) (*orchestrationv2.GetPaymentResponse, error) {
|
|
||||||
return &orchestrationv2.GetPaymentResponse{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*fakeExecutionClientForBatch) Close() error { return nil }
|
func (*fakeExecutionClientForBatch) Close() error { return nil }
|
||||||
|
|
||||||
type fakeEnforcerForBatch struct {
|
type fakeEnforcerForBatch struct {
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import (
|
|||||||
type executionClient interface {
|
type executionClient interface {
|
||||||
ExecutePayment(ctx context.Context, req *orchestrationv2.ExecutePaymentRequest) (*orchestrationv2.ExecutePaymentResponse, error)
|
ExecutePayment(ctx context.Context, req *orchestrationv2.ExecutePaymentRequest) (*orchestrationv2.ExecutePaymentResponse, error)
|
||||||
ExecuteBatchPayment(ctx context.Context, req *orchestrationv2.ExecuteBatchPaymentRequest) (*orchestrationv2.ExecuteBatchPaymentResponse, error)
|
ExecuteBatchPayment(ctx context.Context, req *orchestrationv2.ExecuteBatchPaymentRequest) (*orchestrationv2.ExecuteBatchPaymentResponse, error)
|
||||||
GetPayment(ctx context.Context, req *orchestrationv2.GetPaymentRequest) (*orchestrationv2.GetPaymentResponse, error)
|
|
||||||
ListPayments(ctx context.Context, req *orchestrationv2.ListPaymentsRequest) (*orchestrationv2.ListPaymentsResponse, error)
|
ListPayments(ctx context.Context, req *orchestrationv2.ListPaymentsRequest) (*orchestrationv2.ListPaymentsResponse, error)
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -212,8 +212,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ require (
|
|||||||
github.com/tech/sendico/fx/storage v0.0.0
|
github.com/tech/sendico/fx/storage v0.0.0
|
||||||
github.com/tech/sendico/pkg v0.1.0
|
github.com/tech/sendico/pkg v0.1.0
|
||||||
go.uber.org/zap v1.27.1
|
go.uber.org/zap v1.27.1
|
||||||
golang.org/x/net v0.52.0
|
golang.org/x/net v0.51.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -179,8 +179,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -179,8 +179,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -181,8 +181,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ require (
|
|||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -322,8 +322,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -179,8 +179,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -181,8 +181,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -179,8 +179,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ require (
|
|||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -337,8 +337,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -181,8 +181,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c // indirect
|
||||||
|
|||||||
@@ -196,8 +196,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -181,8 +181,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -182,8 +182,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -182,8 +182,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ require (
|
|||||||
go.opentelemetry.io/otel/trace v1.39.0 // indirect
|
go.opentelemetry.io/otel/trace v1.39.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||||
golang.org/x/net v0.52.0 // indirect
|
golang.org/x/net v0.51.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
|
|||||||
@@ -289,8 +289,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
|||||||
@@ -48,6 +48,4 @@ message GetOperationDocumentRequest {
|
|||||||
|
|
||||||
int64 started_at_unix_ms = 12;
|
int64 started_at_unix_ms = 12;
|
||||||
int64 completed_at_unix_ms = 13;
|
int64 completed_at_unix_ms = 13;
|
||||||
string client_name = 14;
|
|
||||||
string client_address = 15;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
import 'package:pshared/data/dto/money.dart';
|
import 'package:pshared/data/dto/money.dart';
|
||||||
import 'package:pshared/models/money.dart';
|
import 'package:pshared/utils/money.dart';
|
||||||
|
|
||||||
|
|
||||||
extension MoneyMapper on Money {
|
extension MoneyMapper on Money {
|
||||||
MoneyDTO toDTO() => MoneyDTO(
|
MoneyDTO toDTO() =>
|
||||||
amount: amount,
|
MoneyDTO(amount: toDecimal().toString(), currency: currency.isoCode);
|
||||||
currency: currency,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MoneyDTOMapper on MoneyDTO {
|
extension MoneyDTOMapper on MoneyDTO {
|
||||||
Money toDomain() => Money(
|
Money toDomain() {
|
||||||
amount: amount,
|
final parsed = parseMoneyWithCurrencyCode(amount, currency);
|
||||||
currency: currency,
|
if (parsed == null) {
|
||||||
);
|
throw FormatException('Invalid money dto: $currency $amount');
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import 'package:pshared/data/dto/wallet/chain_asset.dart';
|
|
||||||
import 'package:pshared/data/mapper/payment/enums.dart';
|
|
||||||
import 'package:pshared/models/wallet/chain_asset.dart';
|
|
||||||
|
|
||||||
|
|
||||||
extension ChainAssetDTOMapper on ChainAssetDTO {
|
|
||||||
ChainAsset toDomain() => ChainAsset(
|
|
||||||
chain: chainNetworkFromValue(chain),
|
|
||||||
tokenSymbol: tokenSymbol,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ChainAssetMapper on ChainAsset {
|
|
||||||
ChainAssetDTO toDTO() => ChainAssetDTO(
|
|
||||||
chain: chainNetworkToValue(chain),
|
|
||||||
tokenSymbol: tokenSymbol,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
import 'package:pshared/models/wallet/wallet.dart' as domain;
|
import 'package:pshared/models/wallet/wallet.dart' as domain;
|
||||||
import 'package:pshared/models/payment/wallet.dart';
|
import 'package:pshared/models/payment/wallet.dart';
|
||||||
import 'package:pshared/utils/currency.dart';
|
import 'package:pshared/utils/currency.dart';
|
||||||
import 'package:pshared/utils/money.dart';
|
|
||||||
|
|
||||||
|
|
||||||
extension WalletUiMapper on domain.WalletModel {
|
extension WalletUiMapper on domain.WalletModel {
|
||||||
Wallet toUi() => Wallet(
|
Wallet toUi() => Wallet(
|
||||||
id: walletRef,
|
id: walletRef,
|
||||||
walletUserID: walletRef,
|
walletUserID: walletRef,
|
||||||
balance: parseMoneyAmount(
|
balance: availableMoney?.toDouble() ?? balance?.available?.toDouble() ?? 0,
|
||||||
availableMoney?.amount ?? balance?.available?.amount,
|
|
||||||
),
|
|
||||||
currency: currencyStringToCode(asset.tokenSymbol),
|
currency: currencyStringToCode(asset.tokenSymbol),
|
||||||
calculatedAt: balance?.calculatedAt ?? DateTime.now(),
|
calculatedAt: balance?.calculatedAt ?? DateTime.now(),
|
||||||
depositAddress: depositAddress,
|
depositAddress: depositAddress,
|
||||||
|
|||||||
@@ -16,15 +16,19 @@ extension WalletDTOMapper on WalletDTO {
|
|||||||
depositAddress: depositAddress,
|
depositAddress: depositAddress,
|
||||||
status: status,
|
status: status,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
createdAt: (createdAt == null || createdAt!.isEmpty) ? null : DateTime.tryParse(createdAt!),
|
createdAt: (createdAt == null || createdAt!.isEmpty)
|
||||||
updatedAt: (updatedAt == null || updatedAt!.isEmpty) ? null : DateTime.tryParse(updatedAt!),
|
? null
|
||||||
|
: DateTime.tryParse(createdAt!),
|
||||||
|
updatedAt: (updatedAt == null || updatedAt!.isEmpty)
|
||||||
|
? null
|
||||||
|
: DateTime.tryParse(updatedAt!),
|
||||||
balance: balance?.toDomain(),
|
balance: balance?.toDomain(),
|
||||||
availableMoney: balance?.available?.toDomain(),
|
availableMoney: balance?.available?.toDomain(),
|
||||||
describable: newDescribable(
|
describable: newDescribable(
|
||||||
name: name.isNotEmpty ? name : (metadata?['name']?.toString() ?? ''),
|
name: name.isNotEmpty ? name : (metadata?['name']?.toString() ?? ''),
|
||||||
description: (description != null && description!.isNotEmpty)
|
description: (description != null && description!.isNotEmpty)
|
||||||
? description
|
? description
|
||||||
: metadata?['description'],
|
: metadata?['description'],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import 'package:pshared/models/currency.dart';
|
|
||||||
import 'package:pshared/utils/currency.dart';
|
|
||||||
|
|
||||||
|
|
||||||
class Asset {
|
|
||||||
final Currency currency;
|
|
||||||
final double amount;
|
|
||||||
|
|
||||||
const Asset({
|
|
||||||
required this.currency,
|
|
||||||
required this.amount,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Asset createAsset(String currencyCode, String amount) => Asset(
|
|
||||||
currency: currencyStringToCode(currencyCode),
|
|
||||||
amount: double.parse(amount),
|
|
||||||
);
|
|
||||||
@@ -1 +1 @@
|
|||||||
enum Currency {usd, eur, rub, usdt, usdc}
|
enum CurrencyCode {usd, eur, rub, usdt, usdc}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:pshared/models/money.dart';
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
|
|
||||||
class LedgerBalance {
|
class LedgerBalance {
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
class Money {
|
|
||||||
final String amount;
|
|
||||||
final String currency;
|
|
||||||
|
|
||||||
const Money({
|
|
||||||
required this.amount,
|
|
||||||
required this.currency,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:pshared/models/money.dart';
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
|
|
||||||
class PaymentExecutionOperation {
|
class PaymentExecutionOperation {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:pshared/models/money.dart';
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
|
|
||||||
class FeeLine {
|
class FeeLine {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:pshared/models/money.dart';
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
|
|
||||||
class FxQuote {
|
class FxQuote {
|
||||||
final String? quoteRef;
|
final String? quoteRef;
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/payment/fees/treatment.dart';
|
import 'package:pshared/models/payment/fees/treatment.dart';
|
||||||
import 'package:pshared/models/payment/fx/intent.dart';
|
import 'package:pshared/models/payment/fx/intent.dart';
|
||||||
import 'package:pshared/models/payment/kind.dart';
|
import 'package:pshared/models/payment/kind.dart';
|
||||||
import 'package:pshared/models/payment/customer.dart';
|
import 'package:pshared/models/payment/customer.dart';
|
||||||
import 'package:pshared/models/payment/methods/data.dart';
|
import 'package:pshared/models/payment/methods/data.dart';
|
||||||
import 'package:pshared/models/money.dart';
|
|
||||||
import 'package:pshared/models/payment/settlement_mode.dart';
|
import 'package:pshared/models/payment/settlement_mode.dart';
|
||||||
|
|
||||||
|
|
||||||
class PaymentIntent {
|
class PaymentIntent {
|
||||||
final PaymentKind kind;
|
final PaymentKind kind;
|
||||||
final String? sourceRef;
|
final String? sourceRef;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:pshared/models/money.dart';
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
|
|
||||||
class QuoteAmounts {
|
class QuoteAmounts {
|
||||||
final Money? sourcePrincipal;
|
final Money? sourcePrincipal;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ class Wallet implements Describable {
|
|||||||
final String id;
|
final String id;
|
||||||
final String walletUserID; // ID or number that we show the user
|
final String walletUserID; // ID or number that we show the user
|
||||||
final double balance;
|
final double balance;
|
||||||
final Currency currency;
|
final CurrencyCode currency;
|
||||||
final DateTime calculatedAt;
|
final DateTime calculatedAt;
|
||||||
final String? depositAddress;
|
final String? depositAddress;
|
||||||
final ChainNetwork? network;
|
final ChainNetwork? network;
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import 'package:pshared/models/wallet/chain_asset.dart';
|
import 'package:pshared/models/payment/chain_network.dart';
|
||||||
|
|
||||||
|
|
||||||
class WalletAsset extends ChainAsset {
|
class WalletAsset {
|
||||||
|
final ChainNetwork chain;
|
||||||
|
final String tokenSymbol;
|
||||||
final String contractAddress;
|
final String contractAddress;
|
||||||
|
|
||||||
const WalletAsset({
|
const WalletAsset({
|
||||||
required super.chain,
|
required this.chain,
|
||||||
required super.tokenSymbol,
|
required this.tokenSymbol,
|
||||||
required this.contractAddress,
|
required this.contractAddress,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:pshared/models/money.dart';
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
|
|
||||||
class WalletBalance {
|
class WalletBalance {
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import 'package:pshared/models/payment/chain_network.dart';
|
|
||||||
|
|
||||||
|
|
||||||
class ChainAsset {
|
|
||||||
final ChainNetwork chain;
|
|
||||||
final String tokenSymbol;
|
|
||||||
|
|
||||||
const ChainAsset({
|
|
||||||
required this.chain,
|
|
||||||
required this.tokenSymbol,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:pshared/models/describable.dart';
|
import 'package:pshared/models/describable.dart';
|
||||||
import 'package:pshared/models/money.dart';
|
import 'package:money2/money2.dart';
|
||||||
import 'package:pshared/models/wallet/asset.dart';
|
import 'package:pshared/models/wallet/asset.dart';
|
||||||
import 'package:pshared/models/wallet/balance.dart';
|
import 'package:pshared/models/wallet/balance.dart';
|
||||||
|
|
||||||
@@ -39,9 +39,7 @@ class WalletModel implements Describable {
|
|||||||
required this.describable,
|
required this.describable,
|
||||||
});
|
});
|
||||||
|
|
||||||
WalletModel copyWith({
|
WalletModel copyWith({Describable? describable}) => WalletModel(
|
||||||
Describable? describable,
|
|
||||||
}) => WalletModel(
|
|
||||||
walletRef: walletRef,
|
walletRef: walletRef,
|
||||||
organizationRef: organizationRef,
|
organizationRef: organizationRef,
|
||||||
ownerRef: ownerRef,
|
ownerRef: ownerRef,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import 'package:pshared/provider/resource.dart';
|
|||||||
import 'package:pshared/service/ledger.dart';
|
import 'package:pshared/service/ledger.dart';
|
||||||
import 'package:pshared/utils/exception.dart';
|
import 'package:pshared/utils/exception.dart';
|
||||||
|
|
||||||
|
|
||||||
class LedgerAccountsProvider with ChangeNotifier {
|
class LedgerAccountsProvider with ChangeNotifier {
|
||||||
final LedgerService _service;
|
final LedgerService _service;
|
||||||
OrganizationsProvider? _organizations;
|
OrganizationsProvider? _organizations;
|
||||||
@@ -179,7 +180,7 @@ class LedgerAccountsProvider with ChangeNotifier {
|
|||||||
|
|
||||||
Future<void> create({
|
Future<void> create({
|
||||||
required Describable describable,
|
required Describable describable,
|
||||||
required Currency currency,
|
required CurrencyCode currency,
|
||||||
String? ownerRef,
|
String? ownerRef,
|
||||||
}) async {
|
}) async {
|
||||||
final org = _organizations;
|
final org = _organizations;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
import 'package:pshared/controllers/payment/source.dart';
|
import 'package:pshared/controllers/payment/source.dart';
|
||||||
import 'package:pshared/models/payment/asset.dart';
|
import 'package:pshared/models/payment/asset.dart';
|
||||||
import 'package:pshared/models/payment/chain_network.dart';
|
import 'package:pshared/models/payment/chain_network.dart';
|
||||||
@@ -13,19 +15,18 @@ import 'package:pshared/models/payment/methods/data.dart';
|
|||||||
import 'package:pshared/models/payment/methods/ledger.dart';
|
import 'package:pshared/models/payment/methods/ledger.dart';
|
||||||
import 'package:pshared/models/payment/methods/managed_wallet.dart';
|
import 'package:pshared/models/payment/methods/managed_wallet.dart';
|
||||||
import 'package:pshared/models/payment/methods/type.dart';
|
import 'package:pshared/models/payment/methods/type.dart';
|
||||||
import 'package:pshared/models/money.dart';
|
|
||||||
import 'package:pshared/models/payment/settlement_mode.dart';
|
import 'package:pshared/models/payment/settlement_mode.dart';
|
||||||
import 'package:pshared/models/payment/intent.dart';
|
import 'package:pshared/models/payment/intent.dart';
|
||||||
import 'package:pshared/models/recipient/recipient.dart';
|
import 'package:pshared/models/recipient/recipient.dart';
|
||||||
import 'package:pshared/provider/payment/amount.dart';
|
import 'package:pshared/provider/payment/amount.dart';
|
||||||
import 'package:pshared/provider/payment/flow.dart';
|
import 'package:pshared/provider/payment/flow.dart';
|
||||||
import 'package:pshared/provider/recipient/provider.dart';
|
import 'package:pshared/provider/recipient/provider.dart';
|
||||||
|
import 'package:pshared/utils/currency.dart';
|
||||||
import 'package:pshared/utils/payment/fx_helpers.dart';
|
import 'package:pshared/utils/payment/fx_helpers.dart';
|
||||||
|
|
||||||
|
|
||||||
class QuotationIntentBuilder {
|
class QuotationIntentBuilder {
|
||||||
static const String _settlementCurrency = 'RUB';
|
static const String _settlementCurrency = 'RUB';
|
||||||
static const String _addressBookCustomerFallbackId = 'address_book_customer';
|
|
||||||
|
|
||||||
PaymentIntent? build({
|
PaymentIntent? build({
|
||||||
required PaymentAmountProvider payment,
|
required PaymentAmountProvider payment,
|
||||||
@@ -38,10 +39,9 @@ class QuotationIntentBuilder {
|
|||||||
final paymentData = flow.selectedPaymentData;
|
final paymentData = flow.selectedPaymentData;
|
||||||
final selectedMethod = flow.selectedMethod;
|
final selectedMethod = flow.selectedMethod;
|
||||||
final amountValue = payment.amount;
|
final amountValue = payment.amount;
|
||||||
if (sourceMethod == null || sourceCurrency == null || paymentData == null) {
|
if (sourceCurrency == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (amountValue == null) return null;
|
|
||||||
|
|
||||||
final customer = _buildCustomer(
|
final customer = _buildCustomer(
|
||||||
recipient: recipients.currentObject,
|
recipient: recipients.currentObject,
|
||||||
@@ -51,22 +51,22 @@ class QuotationIntentBuilder {
|
|||||||
final amountCurrency = payment.settlementMode == SettlementMode.fixReceived
|
final amountCurrency = payment.settlementMode == SettlementMode.fixReceived
|
||||||
? _settlementCurrency
|
? _settlementCurrency
|
||||||
: sourceCurrency;
|
: sourceCurrency;
|
||||||
final amount = Money(
|
final currency = money2CurrencyFromCode(amountCurrency);
|
||||||
amount: amountValue.toString(),
|
if (currency == null) return null;
|
||||||
currency: amountCurrency,
|
final amount = amountValue == null
|
||||||
);
|
? null
|
||||||
|
: Money.fromNumWithCurrency(amountValue, currency);
|
||||||
final isLedgerSource = source.selectedLedgerAccount != null;
|
final isLedgerSource = source.selectedLedgerAccount != null;
|
||||||
final isCryptoToCrypto =
|
final isCryptoToCrypto =
|
||||||
paymentData is CryptoAddressPaymentMethod &&
|
paymentData is CryptoAddressPaymentMethod &&
|
||||||
(paymentData.asset?.tokenSymbol ?? '').trim().toUpperCase() ==
|
(paymentData.asset?.tokenSymbol ?? '').trim().toUpperCase() ==
|
||||||
amount.currency;
|
sourceCurrency.trim().toUpperCase();
|
||||||
final fxIntent = _buildFxIntent(
|
final fxIntent = _buildFxIntent(
|
||||||
sourceCurrency: sourceCurrency,
|
sourceCurrency: sourceCurrency,
|
||||||
settlementMode: payment.settlementMode,
|
settlementMode: payment.settlementMode,
|
||||||
isLedgerSource: isLedgerSource,
|
isLedgerSource: isLedgerSource,
|
||||||
enabled: !isCryptoToCrypto,
|
enabled: !isCryptoToCrypto,
|
||||||
);
|
);
|
||||||
final comment = _resolveComment(payment.comment);
|
|
||||||
return PaymentIntent(
|
return PaymentIntent(
|
||||||
kind: PaymentKind.payout,
|
kind: PaymentKind.payout,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
@@ -77,7 +77,7 @@ class QuotationIntentBuilder {
|
|||||||
? FeeTreatment.addToSource
|
? FeeTreatment.addToSource
|
||||||
: FeeTreatment.deductFromDestination,
|
: FeeTreatment.deductFromDestination,
|
||||||
settlementMode: payment.settlementMode,
|
settlementMode: payment.settlementMode,
|
||||||
comment: comment,
|
comment: payment.comment,
|
||||||
customer: customer,
|
customer: customer,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -94,14 +94,9 @@ class QuotationIntentBuilder {
|
|||||||
// BFF maps only settlement currency + fx side, then quotation derives pair.
|
// BFF maps only settlement currency + fx side, then quotation derives pair.
|
||||||
// For ledger this preserves source debit in ledger currency (e.g. USDT).
|
// For ledger this preserves source debit in ledger currency (e.g. USDT).
|
||||||
if (isLedgerSource && settlementMode == SettlementMode.fixReceived) {
|
if (isLedgerSource && settlementMode == SettlementMode.fixReceived) {
|
||||||
final base = sourceCurrency.trim();
|
|
||||||
final quote = _settlementCurrency;
|
|
||||||
if (base.isEmpty || base.toUpperCase() == quote.toUpperCase()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return FxIntent(
|
return FxIntent(
|
||||||
pair: CurrencyPair(base: base, quote: quote),
|
pair: CurrencyPair(base: sourceCurrency, quote: _settlementCurrency),
|
||||||
side: FxSide.sellBaseBuyQuote,
|
side: FxSide.buyBaseSellQuote,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,39 +132,59 @@ class QuotationIntentBuilder {
|
|||||||
required PaymentMethod? method,
|
required PaymentMethod? method,
|
||||||
required PaymentMethodData? data,
|
required PaymentMethodData? data,
|
||||||
}) {
|
}) {
|
||||||
final name = recipient?.name.trim();
|
final customerId = recipient?.id.trim() ?? '';
|
||||||
if (name == null || name.isEmpty) return null;
|
final card = _resolveCard(method: method, data: data);
|
||||||
final id = recipient?.id.trim();
|
final fromRecipient = _buildCustomerFromName(
|
||||||
final customerId = id == null || id.isEmpty
|
customerId: customerId,
|
||||||
? _addressBookCustomerFallbackId
|
fullName: recipient?.name,
|
||||||
: id;
|
country: card?.country,
|
||||||
|
);
|
||||||
|
if (fromRecipient != null) return fromRecipient;
|
||||||
|
|
||||||
final parts = name.split(RegExp(r'\s+'));
|
if (card != null) {
|
||||||
|
final firstName = _normalizedOrNull(card.firstName);
|
||||||
|
final lastName = _normalizedOrNull(card.lastName);
|
||||||
|
if (firstName == null && lastName == null) return null;
|
||||||
|
return Customer(
|
||||||
|
id: customerId,
|
||||||
|
firstName: firstName,
|
||||||
|
lastName: lastName,
|
||||||
|
country: card.country,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CardPaymentMethod? _resolveCard({
|
||||||
|
required PaymentMethod? method,
|
||||||
|
required PaymentMethodData? data,
|
||||||
|
}) => method?.cardData ?? (data is CardPaymentMethod ? data : null);
|
||||||
|
|
||||||
|
Customer? _buildCustomerFromName({
|
||||||
|
required String customerId,
|
||||||
|
required String? fullName,
|
||||||
|
String? country,
|
||||||
|
}) {
|
||||||
|
final normalizedName = _normalizedOrNull(fullName);
|
||||||
|
if (normalizedName == null) return null;
|
||||||
|
final parts = normalizedName.split(RegExp(r'\s+'));
|
||||||
final firstName = parts.isNotEmpty ? parts.first : null;
|
final firstName = parts.isNotEmpty ? parts.first : null;
|
||||||
final lastName = parts.length >= 2 ? parts.last : null;
|
final lastName = parts.length >= 2 ? parts.last : null;
|
||||||
final middleName = parts.length > 2
|
final middleName = parts.length > 2
|
||||||
? parts.sublist(1, parts.length - 1).join(' ')
|
? parts.sublist(1, parts.length - 1).join(' ')
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return Customer(
|
return Customer(
|
||||||
id: customerId,
|
id: customerId,
|
||||||
firstName: firstName,
|
firstName: firstName,
|
||||||
middleName: middleName,
|
middleName: middleName,
|
||||||
lastName: lastName,
|
lastName: lastName,
|
||||||
country: _resolveCustomerCountry(method: method, data: data),
|
country: country,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _resolveCustomerCountry({
|
String? _normalizedOrNull(String? value) {
|
||||||
required PaymentMethod? method,
|
if (value == null) return null;
|
||||||
required PaymentMethodData? data,
|
final normalized = value.trim();
|
||||||
}) {
|
|
||||||
final card = method?.cardData ?? (data is CardPaymentMethod ? data : null);
|
|
||||||
return card?.country;
|
|
||||||
}
|
|
||||||
|
|
||||||
String? _resolveComment(String comment) {
|
|
||||||
final normalized = comment.trim();
|
|
||||||
return normalized.isEmpty ? null : normalized;
|
return normalized.isEmpty ? null : normalized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import 'package:logging/logging.dart';
|
|||||||
|
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
import 'package:pshared/api/requests/payment/quote.dart';
|
import 'package:pshared/api/requests/payment/quote.dart';
|
||||||
import 'package:pshared/controllers/payment/source.dart';
|
import 'package:pshared/controllers/payment/source.dart';
|
||||||
import 'package:pshared/data/mapper/payment/intent/payment.dart';
|
import 'package:pshared/data/mapper/payment/intent/payment.dart';
|
||||||
import 'package:pshared/models/asset.dart';
|
|
||||||
import 'package:pshared/models/payment/intent.dart';
|
import 'package:pshared/models/payment/intent.dart';
|
||||||
import 'package:pshared/models/payment/quote/quote.dart';
|
import 'package:pshared/models/payment/quote/quote.dart';
|
||||||
import 'package:pshared/models/money.dart';
|
|
||||||
import 'package:pshared/models/auto_refresh_mode.dart';
|
import 'package:pshared/models/auto_refresh_mode.dart';
|
||||||
import 'package:pshared/provider/organizations.dart';
|
import 'package:pshared/provider/organizations.dart';
|
||||||
import 'package:pshared/provider/payment/amount.dart';
|
import 'package:pshared/provider/payment/amount.dart';
|
||||||
@@ -79,20 +79,12 @@ class QuotationProvider extends ChangeNotifier {
|
|||||||
return DateTime.fromMillisecondsSinceEpoch(expiresAtUnixMs, isUtc: true);
|
return DateTime.fromMillisecondsSinceEpoch(expiresAtUnixMs, isUtc: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Asset? get fee => _assetFromMoney(quoteFeeTotal(quotation));
|
Money? get fee => quoteFeeTotal(quotation);
|
||||||
Asset? get total => _assetFromMoney(
|
Money? get total => quoteSourceDebitTotal(
|
||||||
quoteSourceDebitTotal(
|
quotation,
|
||||||
quotation,
|
preferredSourceCurrency: _sourceCurrencyCode,
|
||||||
preferredSourceCurrency: _sourceCurrencyCode,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
Asset? get recipientGets =>
|
Money? get recipientGets => quotation?.amounts?.destinationSettlement;
|
||||||
_assetFromMoney(quotation?.amounts?.destinationSettlement);
|
|
||||||
|
|
||||||
Asset? _assetFromMoney(Money? money) {
|
|
||||||
if (money == null) return null;
|
|
||||||
return createAsset(money.currency, money.amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _setResource(Resource<PaymentQuote> quotation) {
|
void _setResource(Resource<PaymentQuote> quotation) {
|
||||||
_quotation = quotation;
|
_quotation = quotation;
|
||||||
|
|||||||
@@ -5,14 +5,16 @@ import 'package:flutter/foundation.dart';
|
|||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/models/currency.dart';
|
||||||
import 'package:pshared/models/describable.dart';
|
import 'package:pshared/models/describable.dart';
|
||||||
|
import 'package:pshared/models/payment/chain_network.dart';
|
||||||
import 'package:pshared/models/payment/wallet.dart';
|
import 'package:pshared/models/payment/wallet.dart';
|
||||||
import 'package:pshared/models/wallet/chain_asset.dart';
|
|
||||||
import 'package:pshared/provider/organizations.dart';
|
import 'package:pshared/provider/organizations.dart';
|
||||||
import 'package:pshared/provider/resource.dart';
|
import 'package:pshared/provider/resource.dart';
|
||||||
import 'package:pshared/service/payment/wallets.dart';
|
import 'package:pshared/service/payment/wallets.dart';
|
||||||
import 'package:pshared/utils/exception.dart';
|
import 'package:pshared/utils/exception.dart';
|
||||||
|
|
||||||
|
|
||||||
class WalletsProvider with ChangeNotifier {
|
class WalletsProvider with ChangeNotifier {
|
||||||
final WalletsService _service;
|
final WalletsService _service;
|
||||||
OrganizationsProvider? _organizations;
|
OrganizationsProvider? _organizations;
|
||||||
@@ -180,7 +182,8 @@ class WalletsProvider with ChangeNotifier {
|
|||||||
|
|
||||||
Future<void> create({
|
Future<void> create({
|
||||||
required Describable describable,
|
required Describable describable,
|
||||||
required ChainAsset asset,
|
required ChainNetwork chain,
|
||||||
|
required CurrencyCode currency,
|
||||||
required String? ownerRef,
|
required String? ownerRef,
|
||||||
}) async {
|
}) async {
|
||||||
final org = _organizations;
|
final org = _organizations;
|
||||||
@@ -195,7 +198,8 @@ class WalletsProvider with ChangeNotifier {
|
|||||||
await _service.create(
|
await _service.create(
|
||||||
organizationRef: org.current.id,
|
organizationRef: org.current.id,
|
||||||
describable: describable,
|
describable: describable,
|
||||||
asset: asset,
|
chain: chain,
|
||||||
|
currency: currency,
|
||||||
ownerRef: ownerRef,
|
ownerRef: ownerRef,
|
||||||
);
|
);
|
||||||
await loadWalletsWithBalances();
|
await loadWalletsWithBalances();
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class LedgerService {
|
|||||||
required String organizationRef,
|
required String organizationRef,
|
||||||
required Describable describable,
|
required Describable describable,
|
||||||
required String? ownerRef,
|
required String? ownerRef,
|
||||||
required Currency currency,
|
required CurrencyCode currency,
|
||||||
}) async => AuthorizationService.getPOSTResponse(
|
}) async => AuthorizationService.getPOSTResponse(
|
||||||
_objectType,
|
_objectType,
|
||||||
'/$organizationRef',
|
'/$organizationRef',
|
||||||
|
|||||||
@@ -13,12 +13,10 @@ class PaymentDocumentsService {
|
|||||||
String organizationRef,
|
String organizationRef,
|
||||||
String gatewayService,
|
String gatewayService,
|
||||||
String operationRef,
|
String operationRef,
|
||||||
String paymentRef,
|
|
||||||
) async {
|
) async {
|
||||||
final query = <String, String>{
|
final query = <String, String>{
|
||||||
'gateway_service': gatewayService,
|
'gateway_service': gatewayService,
|
||||||
'operation_ref': operationRef,
|
'operation_ref': operationRef,
|
||||||
'payment_ref': paymentRef,
|
|
||||||
};
|
};
|
||||||
final queryString = Uri(queryParameters: query).query;
|
final queryString = Uri(queryParameters: query).query;
|
||||||
final url = '/documents/operation/$organizationRef?$queryString';
|
final url = '/documents/operation/$organizationRef?$queryString';
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'package:pshared/data/mapper/wallet/ui.dart';
|
import 'package:pshared/data/mapper/wallet/ui.dart';
|
||||||
|
import 'package:pshared/models/currency.dart';
|
||||||
import 'package:pshared/models/describable.dart';
|
import 'package:pshared/models/describable.dart';
|
||||||
|
import 'package:pshared/models/payment/chain_network.dart';
|
||||||
import 'package:pshared/models/payment/wallet.dart';
|
import 'package:pshared/models/payment/wallet.dart';
|
||||||
import 'package:pshared/models/wallet/chain_asset.dart';
|
|
||||||
import 'package:pshared/service/wallet.dart' as shared_wallet_service;
|
import 'package:pshared/service/wallet.dart' as shared_wallet_service;
|
||||||
import 'package:pshared/utils/money.dart';
|
|
||||||
|
|
||||||
|
|
||||||
abstract class WalletsService {
|
abstract class WalletsService {
|
||||||
@@ -12,7 +12,8 @@ abstract class WalletsService {
|
|||||||
Future<void> create({
|
Future<void> create({
|
||||||
required String organizationRef,
|
required String organizationRef,
|
||||||
required Describable describable,
|
required Describable describable,
|
||||||
required ChainAsset asset,
|
required ChainNetwork chain,
|
||||||
|
required CurrencyCode currency,
|
||||||
required String? ownerRef,
|
required String? ownerRef,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -30,19 +31,21 @@ class ApiWalletsService implements WalletsService {
|
|||||||
organizationRef: organizationRef,
|
organizationRef: organizationRef,
|
||||||
walletRef: walletRef,
|
walletRef: walletRef,
|
||||||
);
|
);
|
||||||
return parseMoneyAmount(balance.available?.amount);
|
return balance.available?.toDouble() ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> create({
|
Future<void> create({
|
||||||
required String organizationRef,
|
required String organizationRef,
|
||||||
required Describable describable,
|
required Describable describable,
|
||||||
required ChainAsset asset,
|
required ChainNetwork chain,
|
||||||
|
required CurrencyCode currency,
|
||||||
required String? ownerRef,
|
required String? ownerRef,
|
||||||
}) => shared_wallet_service.WalletService.create(
|
}) => shared_wallet_service.WalletService.create(
|
||||||
organizationRef: organizationRef,
|
organizationRef: organizationRef,
|
||||||
describable: describable,
|
describable: describable,
|
||||||
asset: asset,
|
chain: chain,
|
||||||
|
currency: currency,
|
||||||
ownerRef: ownerRef,
|
ownerRef: ownerRef,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
import 'package:pshared/api/requests/wallet/create.dart';
|
import 'package:pshared/api/requests/wallet/create.dart';
|
||||||
import 'package:pshared/api/responses/wallet_balance.dart';
|
import 'package:pshared/api/responses/wallet_balance.dart';
|
||||||
import 'package:pshared/api/responses/wallets.dart';
|
import 'package:pshared/api/responses/wallets.dart';
|
||||||
|
import 'package:pshared/data/dto/wallet/chain_asset.dart';
|
||||||
import 'package:pshared/data/mapper/describable.dart';
|
import 'package:pshared/data/mapper/describable.dart';
|
||||||
import 'package:pshared/data/mapper/wallet/chain_asset.dart';
|
import 'package:pshared/data/mapper/payment/enums.dart';
|
||||||
import 'package:pshared/data/mapper/wallet/response.dart';
|
import 'package:pshared/data/mapper/wallet/response.dart';
|
||||||
|
import 'package:pshared/models/currency.dart';
|
||||||
import 'package:pshared/models/describable.dart';
|
import 'package:pshared/models/describable.dart';
|
||||||
|
import 'package:pshared/models/payment/chain_network.dart';
|
||||||
import 'package:pshared/models/wallet/balance.dart';
|
import 'package:pshared/models/wallet/balance.dart';
|
||||||
import 'package:pshared/models/wallet/chain_asset.dart';
|
|
||||||
import 'package:pshared/models/wallet/wallet.dart';
|
import 'package:pshared/models/wallet/wallet.dart';
|
||||||
import 'package:pshared/service/authorization/service.dart';
|
import 'package:pshared/service/authorization/service.dart';
|
||||||
import 'package:pshared/service/services.dart';
|
import 'package:pshared/service/services.dart';
|
||||||
|
import 'package:pshared/utils/currency.dart';
|
||||||
|
|
||||||
|
|
||||||
class WalletService {
|
class WalletService {
|
||||||
@@ -37,13 +40,19 @@ class WalletService {
|
|||||||
static Future<void> create({
|
static Future<void> create({
|
||||||
required String organizationRef,
|
required String organizationRef,
|
||||||
required Describable describable,
|
required Describable describable,
|
||||||
required ChainAsset asset,
|
required ChainNetwork chain,
|
||||||
|
required CurrencyCode currency,
|
||||||
required String? ownerRef,
|
required String? ownerRef,
|
||||||
}) async => AuthorizationService.getPOSTResponse(
|
}) async => AuthorizationService.getPOSTResponse(
|
||||||
_objectType,
|
_objectType,
|
||||||
'/$organizationRef',
|
'/$organizationRef',
|
||||||
CreateWalletRequest(
|
CreateWalletRequest(
|
||||||
asset: asset.toDTO(),
|
asset: ChainAssetDTO(
|
||||||
|
chain: chainNetworkToValue(chain),
|
||||||
|
tokenSymbol:
|
||||||
|
money2CurrencyFromCode(currencyCodeToString(currency))?.isoCode ??
|
||||||
|
currencyCodeToString(currency),
|
||||||
|
),
|
||||||
describable: describable.toDTO(),
|
describable: describable.toDTO(),
|
||||||
ownerRef: ownerRef,
|
ownerRef: ownerRef,
|
||||||
).toJson(),
|
).toJson(),
|
||||||
|
|||||||
@@ -1,102 +1,78 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/asset.dart';
|
|
||||||
import 'package:pshared/models/currency.dart';
|
import 'package:pshared/models/currency.dart';
|
||||||
|
|
||||||
|
|
||||||
const nonBreakingSpace = '\u00A0';
|
final Currency _usdtCurrency = Currency.create(
|
||||||
|
'USDT',
|
||||||
|
6,
|
||||||
|
symbol: '₮',
|
||||||
|
isIso: false,
|
||||||
|
country: 'Digital',
|
||||||
|
unit: 'Tether',
|
||||||
|
name: 'Tether',
|
||||||
|
);
|
||||||
|
|
||||||
String withTrailingNonBreakingSpace(String value) {
|
final Currency _usdcCurrency = Currency.create(
|
||||||
return '$value$nonBreakingSpace';
|
'USDC',
|
||||||
}
|
6,
|
||||||
|
symbol: r'($)',
|
||||||
|
isIso: false,
|
||||||
|
country: 'Digital',
|
||||||
|
unit: 'USD Coin',
|
||||||
|
name: 'USD Coin',
|
||||||
|
);
|
||||||
|
|
||||||
String joinWithNonBreakingSpace(String left, String right) {
|
final Map<String, Currency> _commonCurrenciesByCode =
|
||||||
return '$left$nonBreakingSpace$right';
|
<String, Currency>{
|
||||||
}
|
for (final currency in CommonCurrencies().asList())
|
||||||
|
currency.isoCode: currency,
|
||||||
|
_usdtCurrency.isoCode: _usdtCurrency,
|
||||||
|
_usdcCurrency.isoCode: _usdcCurrency,
|
||||||
|
};
|
||||||
|
|
||||||
String currencyCodeToSymbol(Currency currencyCode) {
|
String currencyCodeToSymbol(CurrencyCode currencyCode) {
|
||||||
switch (currencyCode) {
|
final symbol = currencySymbolFromCode(currencyCodeToString(currencyCode));
|
||||||
case Currency.usd:
|
if (symbol == null || symbol.trim().isEmpty) {
|
||||||
return '\$';
|
return currencyCodeToString(currencyCode);
|
||||||
case Currency.usdt:
|
|
||||||
return '₮';
|
|
||||||
case Currency.usdc:
|
|
||||||
return '\$';
|
|
||||||
case Currency.rub:
|
|
||||||
return '₽';
|
|
||||||
case Currency.eur:
|
|
||||||
return '€';
|
|
||||||
}
|
}
|
||||||
|
return symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
String amountToString(double amount) {
|
String currencyToString(CurrencyCode currencyCode, double amount) {
|
||||||
return amount.toStringAsFixed(2);
|
final code = currencyCodeToString(currencyCode);
|
||||||
}
|
final currency = money2CurrencyFromCode(code);
|
||||||
|
if (currency == null) {
|
||||||
String currencyToString(Currency currencyCode, double amount) {
|
return '$amount $code';
|
||||||
return joinWithNonBreakingSpace(
|
|
||||||
currencyCodeToSymbol(currencyCode),
|
|
||||||
amountToString(amount),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String assetToString(Asset asset) {
|
|
||||||
return currencyToString(asset.currency, asset.amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
Currency currencyStringToCode(String currencyCode) {
|
|
||||||
switch (currencyCode) {
|
|
||||||
case 'USD':
|
|
||||||
return Currency.usd;
|
|
||||||
case 'USDT':
|
|
||||||
return Currency.usdt;
|
|
||||||
case 'USDC':
|
|
||||||
return Currency.usdc;
|
|
||||||
case 'RUB':
|
|
||||||
return Currency.rub;
|
|
||||||
case 'EUR':
|
|
||||||
return Currency.eur;
|
|
||||||
default:
|
|
||||||
throw ArgumentError('Unknown currency code: $currencyCode');
|
|
||||||
}
|
}
|
||||||
|
final money = Money.fromNumWithCurrency(amount, currency);
|
||||||
|
return money.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
String currencyCodeToString(Currency currencyCode) {
|
CurrencyCode currencyStringToCode(String currencyCode) {
|
||||||
switch (currencyCode) {
|
final normalized = currencyCode.trim().toUpperCase();
|
||||||
case Currency.usd:
|
for (final value in CurrencyCode.values) {
|
||||||
return 'USD';
|
if (currencyCodeToString(value) == normalized) {
|
||||||
case Currency.usdt:
|
return value;
|
||||||
return 'USDT';
|
}
|
||||||
case Currency.usdc:
|
|
||||||
return 'USDC';
|
|
||||||
case Currency.rub:
|
|
||||||
return 'RUB';
|
|
||||||
case Currency.eur:
|
|
||||||
return 'EUR';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw ArgumentError('Unknown currency code: $currencyCode');
|
||||||
}
|
}
|
||||||
|
|
||||||
IconData iconForCurrencyType(Currency currencyCode) {
|
String currencyCodeToString(CurrencyCode currencyCode) {
|
||||||
switch (currencyCode) {
|
return currencyCode.name.toUpperCase();
|
||||||
case Currency.usd:
|
|
||||||
return Icons.currency_exchange;
|
|
||||||
case Currency.eur:
|
|
||||||
return Icons.currency_exchange;
|
|
||||||
case Currency.rub:
|
|
||||||
return Icons.currency_ruble;
|
|
||||||
case Currency.usdt:
|
|
||||||
return Icons.currency_exchange;
|
|
||||||
case Currency.usdc:
|
|
||||||
return Icons.money;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String? currencySymbolFromCode(String? code) {
|
String? currencySymbolFromCode(String? code) {
|
||||||
final normalized = code?.trim();
|
final currency = money2CurrencyFromCode(code);
|
||||||
if (normalized == null || normalized.isEmpty) return null;
|
if (currency == null) return null;
|
||||||
try {
|
final symbol = currency.symbol.trim();
|
||||||
return currencyCodeToSymbol(currencyStringToCode(normalized.toUpperCase()));
|
return symbol.isEmpty ? null : symbol;
|
||||||
} catch (_) {
|
}
|
||||||
return null;
|
|
||||||
}
|
Currency? money2CurrencyFromCode(String? code) {
|
||||||
|
final normalized = code?.trim().toUpperCase();
|
||||||
|
if (normalized == null || normalized.isEmpty) return null;
|
||||||
|
return _commonCurrenciesByCode[normalized] ?? Currencies().find(normalized);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,35 @@
|
|||||||
import 'package:pshared/models/money.dart';
|
import 'package:money2/money2.dart';
|
||||||
import 'package:pshared/utils/currency.dart';
|
import 'package:pshared/utils/currency.dart';
|
||||||
|
|
||||||
|
const String _decimalMoneyPattern = '0.##################';
|
||||||
|
|
||||||
double parseMoneyAmount(String? raw, {double fallback = 0}) {
|
Money? parseMoneyWithCurrency(String? amount, Currency? currency) {
|
||||||
final trimmed = raw?.trim();
|
if (currency == null) return null;
|
||||||
if (trimmed == null || trimmed.isEmpty) return fallback;
|
final value = _normalizeMoneyAmount(amount);
|
||||||
return double.tryParse(trimmed) ?? fallback;
|
if (value == null || value.isEmpty) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return Money.parseWithCurrency(
|
||||||
|
value,
|
||||||
|
currency,
|
||||||
|
pattern: _decimalMoneyPattern,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String formatMoneyDisplay(
|
Money? parseMoneyWithCurrencyCode(String? amount, String? currencyCode) {
|
||||||
Money? money, {
|
return parseMoneyWithCurrency(amount, money2CurrencyFromCode(currencyCode));
|
||||||
String fallback = '--',
|
|
||||||
String separator = ' ',
|
|
||||||
String invalidAmountFallback = '',
|
|
||||||
}) {
|
|
||||||
if (money == null) return fallback;
|
|
||||||
|
|
||||||
final rawAmount = money.amount.trim();
|
|
||||||
final rawCurrency = money.currency.trim();
|
|
||||||
final parsedAmount = parseMoneyAmount(rawAmount, fallback: double.nan);
|
|
||||||
final amountToken = parsedAmount.isNaN
|
|
||||||
? (rawAmount.isEmpty ? invalidAmountFallback : rawAmount)
|
|
||||||
: amountToString(parsedAmount);
|
|
||||||
|
|
||||||
final symbol = currencySymbolFromCode(rawCurrency);
|
|
||||||
final normalizedSymbol = symbol?.trim() ?? '';
|
|
||||||
final hasSymbol = normalizedSymbol.isNotEmpty;
|
|
||||||
final currencyToken = hasSymbol ? normalizedSymbol : rawCurrency;
|
|
||||||
final first = amountToken;
|
|
||||||
final second = currencyToken;
|
|
||||||
|
|
||||||
if (first.isEmpty && second.isEmpty) return fallback;
|
|
||||||
if (first.isEmpty) return second;
|
|
||||||
if (second.isEmpty) return first;
|
|
||||||
return '$first$separator$second';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MoneyAmountX on Money {
|
String? _normalizeMoneyAmount(String? value) {
|
||||||
double get amountValue => parseMoneyAmount(amount);
|
final normalized = value?.trim();
|
||||||
|
if (normalized == null || normalized.isEmpty) return null;
|
||||||
|
|
||||||
|
if (normalized.contains(',') && !normalized.contains('.')) {
|
||||||
|
return normalized.replaceAll(',', '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/payment/currency_pair.dart';
|
import 'package:pshared/models/payment/currency_pair.dart';
|
||||||
import 'package:pshared/models/payment/fx/intent.dart';
|
import 'package:pshared/models/payment/fx/intent.dart';
|
||||||
import 'package:pshared/models/payment/fx/side.dart';
|
import 'package:pshared/models/payment/fx/side.dart';
|
||||||
import 'package:pshared/models/money.dart';
|
|
||||||
|
|
||||||
|
|
||||||
class FxIntentHelper {
|
class FxIntentHelper {
|
||||||
@@ -37,11 +38,15 @@ class FxIntentHelper {
|
|||||||
case FxSide.unspecified:
|
case FxSide.unspecified:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (amount.currency == pair.base && pair.quote.isNotEmpty) return pair.quote;
|
if (amount.currency.isoCode == pair.base && pair.quote.isNotEmpty) {
|
||||||
if (amount.currency == pair.quote && pair.base.isNotEmpty) return pair.base;
|
return pair.quote;
|
||||||
|
}
|
||||||
|
if (amount.currency.isoCode == pair.quote && pair.base.isNotEmpty) {
|
||||||
|
return pair.base;
|
||||||
|
}
|
||||||
if (pair.quote.isNotEmpty) return pair.quote;
|
if (pair.quote.isNotEmpty) return pair.quote;
|
||||||
if (pair.base.isNotEmpty) return pair.base;
|
if (pair.base.isNotEmpty) return pair.base;
|
||||||
}
|
}
|
||||||
return amount.currency;
|
return amount.currency.isoCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class PaymentQuotationCurrencyResolver {
|
|||||||
PaymentMethodData? paymentData,
|
PaymentMethodData? paymentData,
|
||||||
}) {
|
}) {
|
||||||
final quoteCurrency = _normalizeCurrency(
|
final quoteCurrency = _normalizeCurrency(
|
||||||
quote?.amounts?.destinationSettlement?.currency,
|
quote?.amounts?.destinationSettlement?.currency.isoCode,
|
||||||
);
|
);
|
||||||
if (quoteCurrency != null) return quoteCurrency;
|
if (quoteCurrency != null) return quoteCurrency;
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
import 'package:pshared/models/money.dart';
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/payment/fees/line.dart';
|
import 'package:pshared/models/payment/fees/line.dart';
|
||||||
import 'package:pshared/models/payment/quote/quote.dart';
|
import 'package:pshared/models/payment/quote/quote.dart';
|
||||||
import 'package:pshared/utils/currency.dart';
|
import 'package:pshared/utils/currency.dart';
|
||||||
import 'package:pshared/utils/money.dart';
|
|
||||||
|
|
||||||
|
|
||||||
Money? quoteFeeTotal(PaymentQuote? quote) {
|
Money? quoteFeeTotal(PaymentQuote? quote) {
|
||||||
final preferredCurrency =
|
|
||||||
quote?.amounts?.sourcePrincipal?.currency ??
|
|
||||||
quote?.amounts?.sourceDebitTotal?.currency;
|
|
||||||
return quoteFeeTotalFromLines(
|
return quoteFeeTotalFromLines(
|
||||||
quote?.fees?.lines,
|
quote?.fees?.lines,
|
||||||
preferredCurrency: preferredCurrency,
|
preferredCurrency:
|
||||||
|
quote?.amounts?.sourcePrincipal?.currency.isoCode ??
|
||||||
|
quote?.amounts?.sourceDebitTotal?.currency.isoCode,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,7 +20,8 @@ Money? quoteSourceDebitTotal(
|
|||||||
}) {
|
}) {
|
||||||
final sourceDebitTotal = quote?.amounts?.sourceDebitTotal;
|
final sourceDebitTotal = quote?.amounts?.sourceDebitTotal;
|
||||||
final preferredCurrency = _normalizeCurrency(
|
final preferredCurrency = _normalizeCurrency(
|
||||||
preferredSourceCurrency ?? quote?.amounts?.sourcePrincipal?.currency,
|
preferredSourceCurrency ??
|
||||||
|
quote?.amounts?.sourcePrincipal?.currency.isoCode,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (sourceDebitTotal == null) {
|
if (sourceDebitTotal == null) {
|
||||||
@@ -31,10 +31,9 @@ Money? quoteSourceDebitTotal(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final debitCurrency = _normalizeCurrency(sourceDebitTotal.currency);
|
|
||||||
if (preferredCurrency == null ||
|
if (preferredCurrency == null ||
|
||||||
debitCurrency == null ||
|
_normalizeCurrency(sourceDebitTotal.currency.isoCode) ==
|
||||||
debitCurrency == preferredCurrency) {
|
preferredCurrency) {
|
||||||
return sourceDebitTotal;
|
return sourceDebitTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,22 +51,18 @@ Money? quoteFeeTotalFromLines(
|
|||||||
if (lines == null || lines.isEmpty) return null;
|
if (lines == null || lines.isEmpty) return null;
|
||||||
|
|
||||||
final normalizedPreferred = _normalizeCurrency(preferredCurrency);
|
final normalizedPreferred = _normalizeCurrency(preferredCurrency);
|
||||||
final totalsByCurrency = <String, double>{};
|
final totalsByCurrency = <String, Money>{};
|
||||||
|
|
||||||
for (final line in lines) {
|
for (final line in lines) {
|
||||||
final money = line.amount;
|
final parsedAmount = line.amount;
|
||||||
if (money == null) continue;
|
if (parsedAmount == null) continue;
|
||||||
|
|
||||||
final currency = _normalizeCurrency(money.currency);
|
final currencyCode = parsedAmount.currency.isoCode;
|
||||||
if (currency == null) continue;
|
final signedAmount = _isCreditLine(line.side) ? -parsedAmount : parsedAmount;
|
||||||
|
final current = totalsByCurrency[currencyCode];
|
||||||
final amount = parseMoneyAmount(money.amount, fallback: double.nan);
|
totalsByCurrency[currencyCode] = current == null
|
||||||
if (amount.isNaN) continue;
|
? signedAmount
|
||||||
|
: current + signedAmount;
|
||||||
final sign = _lineSign(line.side);
|
|
||||||
final signedAmount = sign * amount.abs();
|
|
||||||
totalsByCurrency[currency] =
|
|
||||||
(totalsByCurrency[currency] ?? 0) + signedAmount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalsByCurrency.isEmpty) return null;
|
if (totalsByCurrency.isEmpty) return null;
|
||||||
@@ -77,85 +72,59 @@ Money? quoteFeeTotalFromLines(
|
|||||||
totalsByCurrency.containsKey(normalizedPreferred)
|
totalsByCurrency.containsKey(normalizedPreferred)
|
||||||
? normalizedPreferred
|
? normalizedPreferred
|
||||||
: totalsByCurrency.keys.first;
|
: totalsByCurrency.keys.first;
|
||||||
final total = totalsByCurrency[selectedCurrency];
|
return totalsByCurrency[selectedCurrency];
|
||||||
if (total == null) return null;
|
|
||||||
|
|
||||||
return Money(amount: amountToString(total), currency: selectedCurrency);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Money> aggregateMoneyByCurrency(Iterable<Money?> values) {
|
List<Money> aggregateMoneyByCurrency(Iterable<Money?> values) {
|
||||||
final totals = <String, double>{};
|
final totals = <String, Money>{};
|
||||||
for (final value in values) {
|
for (final value in values) {
|
||||||
if (value == null) continue;
|
if (value == null) continue;
|
||||||
|
final currency = value.currency.isoCode;
|
||||||
final currency = _normalizeCurrency(value.currency);
|
final current = totals[currency];
|
||||||
if (currency == null) continue;
|
totals[currency] = current == null ? value : current + value;
|
||||||
|
|
||||||
final amount = parseMoneyAmount(value.amount, fallback: double.nan);
|
|
||||||
if (amount.isNaN) continue;
|
|
||||||
|
|
||||||
totals[currency] = (totals[currency] ?? 0) + amount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return totals.entries
|
return totals.values.toList();
|
||||||
.map(
|
|
||||||
(entry) =>
|
|
||||||
Money(amount: amountToString(entry.value), currency: entry.key),
|
|
||||||
)
|
|
||||||
.toList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Money? _rebuildSourceDebitTotal(
|
Money? _rebuildSourceDebitTotal(
|
||||||
PaymentQuote? quote, {
|
PaymentQuote? quote, {
|
||||||
String? preferredSourceCurrency,
|
String? preferredSourceCurrency,
|
||||||
}) {
|
}) {
|
||||||
final sourcePrincipal = quote?.amounts?.sourcePrincipal;
|
final principal = quote?.amounts?.sourcePrincipal;
|
||||||
if (sourcePrincipal == null) return null;
|
if (principal == null) return null;
|
||||||
|
|
||||||
final principalCurrency = _normalizeCurrency(sourcePrincipal.currency);
|
final principalCurrency = principal.currency.isoCode;
|
||||||
if (principalCurrency == null) return null;
|
|
||||||
if (preferredSourceCurrency != null &&
|
if (preferredSourceCurrency != null &&
|
||||||
principalCurrency != preferredSourceCurrency) {
|
principalCurrency != preferredSourceCurrency) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final principalAmount = parseMoneyAmount(
|
var totalAmount = principal;
|
||||||
sourcePrincipal.amount,
|
|
||||||
fallback: double.nan,
|
|
||||||
);
|
|
||||||
if (principalAmount.isNaN) return null;
|
|
||||||
|
|
||||||
double totalAmount = principalAmount;
|
|
||||||
final fee = quoteFeeTotalFromLines(
|
final fee = quoteFeeTotalFromLines(
|
||||||
quote?.fees?.lines,
|
quote?.fees?.lines,
|
||||||
preferredCurrency: principalCurrency,
|
preferredCurrency: principalCurrency,
|
||||||
);
|
);
|
||||||
if (fee != null && _normalizeCurrency(fee.currency) == principalCurrency) {
|
if (fee != null && fee.currency.isoCode == principalCurrency) {
|
||||||
final feeAmount = parseMoneyAmount(fee.amount, fallback: double.nan);
|
totalAmount += fee;
|
||||||
if (!feeAmount.isNaN) {
|
|
||||||
totalAmount += feeAmount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Money(
|
return totalAmount;
|
||||||
amount: amountToString(totalAmount),
|
|
||||||
currency: principalCurrency,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double _lineSign(String? side) {
|
bool _isCreditLine(String? side) {
|
||||||
final normalized = side?.trim().toLowerCase() ?? '';
|
final normalized = side?.trim().toLowerCase() ?? '';
|
||||||
switch (normalized) {
|
switch (normalized) {
|
||||||
case 'entry_side_credit':
|
case 'entry_side_credit':
|
||||||
case 'credit':
|
case 'credit':
|
||||||
return -1;
|
return true;
|
||||||
default:
|
default:
|
||||||
return 1;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _normalizeCurrency(String? currency) {
|
String? _normalizeCurrency(String? currency) {
|
||||||
final normalized = currency?.trim().toUpperCase();
|
final normalized = currency?.trim();
|
||||||
if (normalized == null || normalized.isEmpty) return null;
|
if (normalized == null || normalized.isEmpty) return null;
|
||||||
return normalized;
|
return money2CurrencyFromCode(normalized)?.isoCode ?? normalized.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ dependencies:
|
|||||||
uuid: ^4.5.1
|
uuid: ^4.5.1
|
||||||
image: ^4.5.4
|
image: ^4.5.4
|
||||||
shared_preferences: ^2.5.3
|
shared_preferences: ^2.5.3
|
||||||
|
money2: ^6.3.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^6.0.0
|
flutter_lints: ^6.0.0
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ RouteBase payoutShellRoute() => ShellRoute(
|
|||||||
path: PayoutRoutes.reportPaymentPath,
|
path: PayoutRoutes.reportPaymentPath,
|
||||||
pageBuilder: (_, state) => NoTransitionPage(
|
pageBuilder: (_, state) => NoTransitionPage(
|
||||||
child: PaymentDetailsPage(
|
child: PaymentDetailsPage(
|
||||||
paymentRef:
|
paymentId:
|
||||||
state.uri.queryParameters[PayoutRoutes.reportPaymentIdQuery] ??
|
state.uri.queryParameters[PayoutRoutes.reportPaymentIdQuery] ??
|
||||||
'',
|
'',
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:pshared/models/payment/status.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/models/wallet/wallet_transaction.dart';
|
|
||||||
import 'package:pweb/providers/wallet_transactions.dart';
|
|
||||||
|
|
||||||
|
|
||||||
class WalletTransactionsController extends ChangeNotifier {
|
|
||||||
List<WalletTransaction> _filteredTransactions = [];
|
|
||||||
DateTimeRange? _dateRange;
|
|
||||||
final Set<OperationStatus> _selectedStatuses = {};
|
|
||||||
final Set<WalletTransactionType> _selectedTypes = {};
|
|
||||||
WalletTransactionsProvider? _provider;
|
|
||||||
|
|
||||||
List<WalletTransaction> get transactions =>
|
|
||||||
_provider?.transactions ?? const [];
|
|
||||||
List<WalletTransaction> get filteredTransactions => _filteredTransactions;
|
|
||||||
DateTimeRange? get dateRange => _dateRange;
|
|
||||||
Set<OperationStatus> get selectedStatuses => _selectedStatuses;
|
|
||||||
Set<WalletTransactionType> get selectedTypes => _selectedTypes;
|
|
||||||
bool get isLoading => _provider?.isLoading ?? false;
|
|
||||||
String? get error => _provider?.error;
|
|
||||||
bool get hasFilters =>
|
|
||||||
_dateRange != null ||
|
|
||||||
_selectedStatuses.isNotEmpty ||
|
|
||||||
_selectedTypes.isNotEmpty;
|
|
||||||
|
|
||||||
void update(WalletTransactionsProvider provider) {
|
|
||||||
if (identical(_provider, provider)) return;
|
|
||||||
_provider?.removeListener(_onProviderChanged);
|
|
||||||
_provider = provider;
|
|
||||||
_provider?.addListener(_onProviderChanged);
|
|
||||||
_rebuildFiltered(notify: false);
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setDateRange(DateTimeRange? range) {
|
|
||||||
_dateRange = range;
|
|
||||||
_rebuildFiltered();
|
|
||||||
}
|
|
||||||
|
|
||||||
void toggleStatus(OperationStatus status) {
|
|
||||||
if (_selectedStatuses.contains(status)) {
|
|
||||||
_selectedStatuses.remove(status);
|
|
||||||
} else {
|
|
||||||
_selectedStatuses.add(status);
|
|
||||||
}
|
|
||||||
_rebuildFiltered();
|
|
||||||
}
|
|
||||||
|
|
||||||
void toggleType(WalletTransactionType type) {
|
|
||||||
if (_selectedTypes.contains(type)) {
|
|
||||||
_selectedTypes.remove(type);
|
|
||||||
} else {
|
|
||||||
_selectedTypes.add(type);
|
|
||||||
}
|
|
||||||
_rebuildFiltered();
|
|
||||||
}
|
|
||||||
|
|
||||||
void resetFilters() {
|
|
||||||
_dateRange = null;
|
|
||||||
_selectedStatuses.clear();
|
|
||||||
_selectedTypes.clear();
|
|
||||||
_rebuildFiltered();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onProviderChanged() {
|
|
||||||
_rebuildFiltered();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _rebuildFiltered({bool notify = true}) {
|
|
||||||
final source = _provider?.transactions ?? const <WalletTransaction>[];
|
|
||||||
final activeWalletId = _provider?.walletId;
|
|
||||||
_filteredTransactions = source.where((tx) {
|
|
||||||
final walletMatch =
|
|
||||||
activeWalletId == null || tx.walletId == activeWalletId;
|
|
||||||
final statusMatch =
|
|
||||||
_selectedStatuses.isEmpty || _selectedStatuses.contains(tx.status);
|
|
||||||
final typeMatch =
|
|
||||||
_selectedTypes.isEmpty || _selectedTypes.contains(tx.type);
|
|
||||||
final dateMatch =
|
|
||||||
_dateRange == null ||
|
|
||||||
(tx.date.isAfter(
|
|
||||||
_dateRange!.start.subtract(const Duration(seconds: 1)),
|
|
||||||
) &&
|
|
||||||
tx.date.isBefore(
|
|
||||||
_dateRange!.end.add(const Duration(seconds: 1)),
|
|
||||||
));
|
|
||||||
|
|
||||||
return walletMatch && statusMatch && typeMatch && dateMatch;
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
if (notify) notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_provider?.removeListener(_onProviderChanged);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:pshared/controllers/payment/source.dart';
|
import 'package:pshared/controllers/payment/source.dart';
|
||||||
import 'package:pshared/models/payment/settlement_mode.dart';
|
import 'package:pshared/models/payment/settlement_mode.dart';
|
||||||
import 'package:pshared/provider/payment/amount.dart';
|
import 'package:pshared/provider/payment/amount.dart';
|
||||||
import 'package:pshared/utils/currency.dart';
|
import 'package:pshared/utils/currency.dart';
|
||||||
import 'package:pshared/utils/money.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/models/payment/amount/mode.dart';
|
import 'package:pweb/models/payment/amount/mode.dart';
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ class PaymentAmountFieldController extends ChangeNotifier {
|
|||||||
|
|
||||||
PaymentAmountFieldController({required double? initialAmount})
|
PaymentAmountFieldController({required double? initialAmount})
|
||||||
: textController = TextEditingController(
|
: textController = TextEditingController(
|
||||||
text: initialAmount == null ? '' : amountToString(initialAmount),
|
text: initialAmount == null ? '' : initialAmount.toString(),
|
||||||
);
|
);
|
||||||
|
|
||||||
PaymentAmountMode get mode => _mode;
|
PaymentAmountMode get mode => _mode;
|
||||||
@@ -122,18 +123,14 @@ class PaymentAmountFieldController extends ChangeNotifier {
|
|||||||
};
|
};
|
||||||
|
|
||||||
double? _parseAmount(String value) {
|
double? _parseAmount(String value) {
|
||||||
final parsed = parseMoneyAmount(
|
return double.tryParse(value.replaceAll(',', '.').trim());
|
||||||
value.replaceAll(',', '.'),
|
|
||||||
fallback: double.nan,
|
|
||||||
);
|
|
||||||
return parsed.isNaN ? null : parsed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _syncTextWithAmount(double? amount) {
|
void _syncTextWithAmount(double? amount) {
|
||||||
final parsedText = _parseAmount(textController.text);
|
final parsedText = _parseAmount(textController.text);
|
||||||
if (parsedText == amount) return;
|
if (parsedText == amount) return;
|
||||||
|
|
||||||
final nextText = amount == null ? '' : amountToString(amount);
|
final nextText = amount == null ? '' : _formatAmount(amount);
|
||||||
_isSyncingText = true;
|
_isSyncingText = true;
|
||||||
textController.value = TextEditingValue(
|
textController.value = TextEditingValue(
|
||||||
text: nextText,
|
text: nextText,
|
||||||
@@ -142,6 +139,12 @@ class PaymentAmountFieldController extends ChangeNotifier {
|
|||||||
_isSyncingText = false;
|
_isSyncingText = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _formatAmount(double amount) {
|
||||||
|
final currency = money2CurrencyFromCode(activeCurrencyCode);
|
||||||
|
if (currency == null) return amount.toString();
|
||||||
|
return Money.fromNumWithCurrency(amount, currency).toDecimal().toString();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_provider?.removeListener(_handleProviderChanged);
|
_provider?.removeListener(_handleProviderChanged);
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ import 'package:pweb/utils/report/operations/document_rule.dart';
|
|||||||
|
|
||||||
|
|
||||||
class PaymentDetailsController extends ChangeNotifier {
|
class PaymentDetailsController extends ChangeNotifier {
|
||||||
PaymentDetailsController({required String paymentRef})
|
PaymentDetailsController({required String paymentId})
|
||||||
: _paymentRef = paymentRef;
|
: _paymentId = paymentId;
|
||||||
|
|
||||||
PaymentsProvider? _payments;
|
PaymentsProvider? _payments;
|
||||||
String _paymentRef;
|
String _paymentId;
|
||||||
Payment? _payment;
|
Payment? _payment;
|
||||||
|
|
||||||
String get paymentId => _paymentRef;
|
String get paymentId => _paymentId;
|
||||||
Payment? get payment => _payment;
|
Payment? get payment => _payment;
|
||||||
bool get isLoading => _payments?.isLoading ?? false;
|
bool get isLoading => _payments?.isLoading ?? false;
|
||||||
Exception? get error => _payments?.error;
|
Exception? get error => _payments?.error;
|
||||||
@@ -44,8 +44,8 @@ class PaymentDetailsController extends ChangeNotifier {
|
|||||||
operationDocumentRequest(operation) != null;
|
operationDocumentRequest(operation) != null;
|
||||||
|
|
||||||
void update(PaymentsProvider provider, String paymentId) {
|
void update(PaymentsProvider provider, String paymentId) {
|
||||||
if (_paymentRef != paymentId) {
|
if (_paymentId != paymentId) {
|
||||||
_paymentRef = paymentId;
|
_paymentId = paymentId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!identical(_payments, provider)) {
|
if (!identical(_payments, provider)) {
|
||||||
@@ -60,7 +60,7 @@ class PaymentDetailsController extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _rebuild() {
|
void _rebuild() {
|
||||||
_payment = _findPayment(_payments?.payments ?? const [], _paymentRef);
|
_payment = _findPayment(_payments?.payments ?? const [], _paymentId);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,5 +29,4 @@ class RecentPaymentsController extends ChangeNotifier {
|
|||||||
_recent = sortOperations(operations).take(5).toList();
|
_recent = sortOperations(operations).take(5).toList();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
import 'package:money2/money2.dart';
|
||||||
|
|
||||||
import 'package:pshared/controllers/payment/source.dart';
|
import 'package:pshared/controllers/payment/source.dart';
|
||||||
import 'package:pshared/models/money.dart';
|
|
||||||
import 'package:pshared/models/payment/asset.dart';
|
import 'package:pshared/models/payment/asset.dart';
|
||||||
import 'package:pshared/models/payment/chain_network.dart';
|
import 'package:pshared/models/payment/chain_network.dart';
|
||||||
import 'package:pshared/models/payment/methods/data.dart';
|
import 'package:pshared/models/payment/methods/data.dart';
|
||||||
@@ -17,6 +18,7 @@ import 'package:pweb/models/payment/multiple_payouts/state.dart';
|
|||||||
import 'package:pweb/providers/multiple_payouts.dart';
|
import 'package:pweb/providers/multiple_payouts.dart';
|
||||||
import 'package:pweb/services/payments/csv_input.dart';
|
import 'package:pweb/services/payments/csv_input.dart';
|
||||||
|
|
||||||
|
|
||||||
class MultiplePayoutsController extends ChangeNotifier {
|
class MultiplePayoutsController extends ChangeNotifier {
|
||||||
final CsvInputService _csvInput;
|
final CsvInputService _csvInput;
|
||||||
MultiplePayoutsProvider? _provider;
|
MultiplePayoutsProvider? _provider;
|
||||||
|
|||||||
@@ -31,10 +31,7 @@ import 'package:pweb/app/app.dart';
|
|||||||
import 'package:pweb/pages/invitations/widgets/list/view_model.dart';
|
import 'package:pweb/pages/invitations/widgets/list/view_model.dart';
|
||||||
import 'package:pweb/app/timeago.dart';
|
import 'package:pweb/app/timeago.dart';
|
||||||
import 'package:pweb/providers/two_factor.dart';
|
import 'package:pweb/providers/two_factor.dart';
|
||||||
import 'package:pweb/controllers/operations/wallet_transactions.dart';
|
|
||||||
import 'package:pweb/providers/wallet_transactions.dart';
|
|
||||||
import 'package:pweb/services/posthog.dart';
|
import 'package:pweb/services/posthog.dart';
|
||||||
import 'package:pweb/services/wallet_transactions.dart';
|
|
||||||
import 'package:pweb/providers/account.dart';
|
import 'package:pweb/providers/account.dart';
|
||||||
import 'package:pweb/providers/locale.dart';
|
import 'package:pweb/providers/locale.dart';
|
||||||
|
|
||||||
@@ -142,18 +139,6 @@ void main() async {
|
|||||||
create: (_) => WalletsController(),
|
create: (_) => WalletsController(),
|
||||||
update: (_, wallets, controller) => controller!..update(wallets),
|
update: (_, wallets, controller) => controller!..update(wallets),
|
||||||
),
|
),
|
||||||
ChangeNotifierProvider(
|
|
||||||
create: (_) => WalletTransactionsProvider(
|
|
||||||
MockWalletTransactionsService(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ChangeNotifierProxyProvider<
|
|
||||||
WalletTransactionsProvider,
|
|
||||||
WalletTransactionsController
|
|
||||||
>(
|
|
||||||
create: (_) => WalletTransactionsController(),
|
|
||||||
update: (_, provider, controller) => controller!..update(provider),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
child: const PayApp(),
|
child: const PayApp(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class WalletTransaction {
|
|||||||
final WalletTransactionType type;
|
final WalletTransactionType type;
|
||||||
final OperationStatus status;
|
final OperationStatus status;
|
||||||
final double amount;
|
final double amount;
|
||||||
final Currency currency;
|
final CurrencyCode currency;
|
||||||
final DateTime date;
|
final DateTime date;
|
||||||
final String description;
|
final String description;
|
||||||
final String? counterparty;
|
final String? counterparty;
|
||||||
@@ -56,7 +56,7 @@ class WalletTransaction {
|
|||||||
WalletTransactionType? type,
|
WalletTransactionType? type,
|
||||||
OperationStatus? status,
|
OperationStatus? status,
|
||||||
double? amount,
|
double? amount,
|
||||||
Currency? currency,
|
CurrencyCode? currency,
|
||||||
DateTime? date,
|
DateTime? date,
|
||||||
String? description,
|
String? description,
|
||||||
String? counterparty,
|
String? counterparty,
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ import 'package:pshared/models/currency.dart';
|
|||||||
import 'package:pshared/models/payment/chain_network.dart';
|
import 'package:pshared/models/payment/chain_network.dart';
|
||||||
|
|
||||||
|
|
||||||
const Currency managedCurrencyDefault = Currency.usdt;
|
const CurrencyCode managedCurrencyDefault = CurrencyCode.usdt;
|
||||||
const Currency ledgerCurrencyDefault = Currency.rub;
|
const CurrencyCode ledgerCurrencyDefault = CurrencyCode.rub;
|
||||||
const ChainNetwork managedNetworkDefault = ChainNetwork.tronMainnet;
|
const ChainNetwork managedNetworkDefault = ChainNetwork.tronMainnet;
|
||||||
|
|||||||
@@ -8,11 +8,9 @@ import 'package:pshared/models/currency.dart';
|
|||||||
import 'package:pshared/models/describable.dart';
|
import 'package:pshared/models/describable.dart';
|
||||||
import 'package:pshared/models/payment/chain_network.dart';
|
import 'package:pshared/models/payment/chain_network.dart';
|
||||||
import 'package:pshared/models/payment/type.dart';
|
import 'package:pshared/models/payment/type.dart';
|
||||||
import 'package:pshared/models/wallet/chain_asset.dart';
|
|
||||||
import 'package:pshared/provider/accounts/employees.dart';
|
import 'package:pshared/provider/accounts/employees.dart';
|
||||||
import 'package:pshared/provider/ledger.dart';
|
import 'package:pshared/provider/ledger.dart';
|
||||||
import 'package:pshared/provider/payment/wallets.dart';
|
import 'package:pshared/provider/payment/wallets.dart';
|
||||||
import 'package:pshared/utils/currency.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/pages/dashboard/buttons/balance/add/form.dart';
|
import 'package:pweb/pages/dashboard/buttons/balance/add/form.dart';
|
||||||
import 'package:pweb/pages/dashboard/buttons/balance/add/constants.dart';
|
import 'package:pweb/pages/dashboard/buttons/balance/add/constants.dart';
|
||||||
@@ -42,9 +40,9 @@ class _AddBalanceDialogState extends State<AddBalanceDialog> {
|
|||||||
|
|
||||||
PaymentType _assetType = PaymentType.managedWallet;
|
PaymentType _assetType = PaymentType.managedWallet;
|
||||||
String? _ownerRef;
|
String? _ownerRef;
|
||||||
Currency _managedCurrency = managedCurrencyDefault;
|
CurrencyCode _managedCurrency = managedCurrencyDefault;
|
||||||
ChainNetwork _network = managedNetworkDefault;
|
ChainNetwork _network = managedNetworkDefault;
|
||||||
Currency _ledgerCurrency = ledgerCurrencyDefault;
|
CurrencyCode _ledgerCurrency = ledgerCurrencyDefault;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@@ -60,7 +58,7 @@ class _AddBalanceDialogState extends State<AddBalanceDialog> {
|
|||||||
|
|
||||||
void _setOwnerRef(String? value) => setState(() => _ownerRef = value);
|
void _setOwnerRef(String? value) => setState(() => _ownerRef = value);
|
||||||
|
|
||||||
void _setManagedCurrency(Currency? value) {
|
void _setManagedCurrency(CurrencyCode? value) {
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
setState(() => _managedCurrency = value);
|
setState(() => _managedCurrency = value);
|
||||||
}
|
}
|
||||||
@@ -70,7 +68,7 @@ class _AddBalanceDialogState extends State<AddBalanceDialog> {
|
|||||||
setState(() => _network = value);
|
setState(() => _network = value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setLedgerCurrency(Currency? value) {
|
void _setLedgerCurrency(CurrencyCode? value) {
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
setState(() => _ledgerCurrency = value);
|
setState(() => _ledgerCurrency = value);
|
||||||
}
|
}
|
||||||
@@ -102,7 +100,8 @@ class _AddBalanceDialogState extends State<AddBalanceDialog> {
|
|||||||
if (_assetType == PaymentType.managedWallet) {
|
if (_assetType == PaymentType.managedWallet) {
|
||||||
await context.read<WalletsProvider>().create(
|
await context.read<WalletsProvider>().create(
|
||||||
describable: newDescribable(name: name, description: description),
|
describable: newDescribable(name: name, description: description),
|
||||||
asset: ChainAsset(chain: _network, tokenSymbol: currencyCodeToString(_managedCurrency)),
|
chain: _network,
|
||||||
|
currency: _managedCurrency,
|
||||||
ownerRef: owner?.id,
|
ownerRef: owner?.id,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ class AddBalanceForm extends StatelessWidget {
|
|||||||
final ValueChanged<String?> onOwnerChanged;
|
final ValueChanged<String?> onOwnerChanged;
|
||||||
final TextEditingController nameController;
|
final TextEditingController nameController;
|
||||||
final TextEditingController descriptionController;
|
final TextEditingController descriptionController;
|
||||||
final Currency managedCurrency;
|
final CurrencyCode managedCurrency;
|
||||||
final ChainNetwork network;
|
final ChainNetwork network;
|
||||||
final Currency ledgerCurrency;
|
final CurrencyCode ledgerCurrency;
|
||||||
final ValueChanged<Currency?> onManagedCurrencyChanged;
|
final ValueChanged<CurrencyCode?> onManagedCurrencyChanged;
|
||||||
final ValueChanged<ChainNetwork?> onNetworkChanged;
|
final ValueChanged<ChainNetwork?> onNetworkChanged;
|
||||||
final ValueChanged<Currency?> onLedgerCurrencyChanged;
|
final ValueChanged<CurrencyCode?> onLedgerCurrencyChanged;
|
||||||
final bool showEmployeesLoading;
|
final bool showEmployeesLoading;
|
||||||
|
|
||||||
const AddBalanceForm({
|
const AddBalanceForm({
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:pshared/models/currency.dart';
|
|||||||
import 'package:pshared/utils/currency.dart';
|
import 'package:pshared/utils/currency.dart';
|
||||||
|
|
||||||
|
|
||||||
DropdownMenuItem<Currency> currencyItem(Currency currency) => DropdownMenuItem(
|
DropdownMenuItem<CurrencyCode> currencyItem(CurrencyCode currency) => DropdownMenuItem(
|
||||||
value: currency,
|
value: currency,
|
||||||
child: Text(currencyCodeToString(currency)),
|
child: Text(currencyCodeToString(currency)),
|
||||||
);
|
);
|
||||||
@@ -10,8 +10,8 @@ import 'package:pweb/generated/i18n/app_localizations.dart';
|
|||||||
|
|
||||||
|
|
||||||
class LedgerFields extends StatelessWidget {
|
class LedgerFields extends StatelessWidget {
|
||||||
final Currency currency;
|
final CurrencyCode currency;
|
||||||
final ValueChanged<Currency?>? onCurrencyChanged;
|
final ValueChanged<CurrencyCode?>? onCurrencyChanged;
|
||||||
|
|
||||||
const LedgerFields({
|
const LedgerFields({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -20,7 +20,7 @@ class LedgerFields extends StatelessWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => DropdownButtonFormField<Currency>(
|
Widget build(BuildContext context) => DropdownButtonFormField<CurrencyCode>(
|
||||||
initialValue: currency,
|
initialValue: currency,
|
||||||
decoration: getInputDecoration(context, AppLocalizations.of(context)!.currency, true),
|
decoration: getInputDecoration(context, AppLocalizations.of(context)!.currency, true),
|
||||||
items: [
|
items: [
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ import 'package:pweb/generated/i18n/app_localizations.dart';
|
|||||||
|
|
||||||
|
|
||||||
class ManagedWalletFields extends StatelessWidget {
|
class ManagedWalletFields extends StatelessWidget {
|
||||||
final Currency currency;
|
final CurrencyCode currency;
|
||||||
final ChainNetwork network;
|
final ChainNetwork network;
|
||||||
final ValueChanged<Currency?>? onCurrencyChanged;
|
final ValueChanged<CurrencyCode?>? onCurrencyChanged;
|
||||||
final ValueChanged<ChainNetwork?>? onNetworkChanged;
|
final ValueChanged<ChainNetwork?>? onNetworkChanged;
|
||||||
|
|
||||||
const ManagedWalletFields({
|
const ManagedWalletFields({
|
||||||
@@ -31,7 +31,7 @@ class ManagedWalletFields extends StatelessWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
spacing: 12,
|
spacing: 12,
|
||||||
children: [
|
children: [
|
||||||
DropdownButtonFormField<Currency>(
|
DropdownButtonFormField<CurrencyCode>(
|
||||||
initialValue: currency,
|
initialValue: currency,
|
||||||
decoration: getInputDecoration(context, l10n.currency, true),
|
decoration: getInputDecoration(context, l10n.currency, true),
|
||||||
items: [
|
items: [
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pshared/controllers/balance_mask/wallets.dart';
|
import 'package:pshared/controllers/balance_mask/wallets.dart';
|
||||||
import 'package:pshared/models/money.dart';
|
|
||||||
import 'package:pshared/models/payment/wallet.dart';
|
import 'package:pshared/models/payment/wallet.dart';
|
||||||
import 'package:pshared/utils/currency.dart';
|
import 'package:pshared/utils/currency.dart';
|
||||||
|
|
||||||
@@ -28,12 +27,10 @@ class BalanceAmount extends StatelessWidget {
|
|||||||
final textTheme = Theme.of(context).textTheme;
|
final textTheme = Theme.of(context).textTheme;
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
final currencyBalance = currencyCodeToSymbol(wallet.currency);
|
final currencyBalance = currencyCodeToSymbol(wallet.currency);
|
||||||
final formattedBalance = formatMoneyUi(
|
final formattedBalance = formatAmountUi(
|
||||||
context,
|
context,
|
||||||
Money(
|
amount: wallet.balance,
|
||||||
amount: amountToString(wallet.balance),
|
currency: currencyCodeToString(wallet.currency),
|
||||||
currency: currencyCodeToString(wallet.currency),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
final wallets = context.watch<WalletsController>();
|
final wallets = context.watch<WalletsController>();
|
||||||
final isMasked = wallets.isBalanceMasked(wallet.id);
|
final isMasked = wallets.isBalanceMasked(wallet.id);
|
||||||
|
|||||||
@@ -36,9 +36,7 @@ class PaymentAmountField extends StatelessWidget {
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: loc.amount,
|
labelText: loc.amount,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
prefixText: symbol == null
|
prefixText: symbol == null ? null : '$symbol ',
|
||||||
? null
|
|
||||||
: withTrailingNonBreakingSpace(symbol),
|
|
||||||
),
|
),
|
||||||
onChanged: ui.handleChanged,
|
onChanged: ui.handleChanged,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:pshared/utils/currency.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/controllers/payouts/multiple_payouts.dart';
|
import 'package:pweb/controllers/payouts/multiple_payouts.dart';
|
||||||
import 'package:pweb/models/dashboard/summary_values.dart';
|
import 'package:pweb/models/dashboard/summary_values.dart';
|
||||||
import 'package:pweb/pages/dashboard/payouts/summary/widget.dart';
|
import 'package:pweb/pages/dashboard/payouts/summary/widget.dart';
|
||||||
@@ -28,21 +26,12 @@ class SourceQuoteSummary extends StatelessWidget {
|
|||||||
values: PaymentSummaryValues(
|
values: PaymentSummaryValues(
|
||||||
fee: controller.aggregateFeeAmount == null
|
fee: controller.aggregateFeeAmount == null
|
||||||
? l10n.noFee
|
? l10n.noFee
|
||||||
: formatMoneyUiWithL10n(
|
: formatMoneyUi(context, controller.aggregateFeeAmount),
|
||||||
l10n,
|
recipientReceives: formatMoneyUi(
|
||||||
controller.aggregateFeeAmount,
|
context,
|
||||||
separator: nonBreakingSpace,
|
|
||||||
),
|
|
||||||
recipientReceives: formatMoneyUiWithL10n(
|
|
||||||
l10n,
|
|
||||||
controller.aggregateSettlementAmount,
|
controller.aggregateSettlementAmount,
|
||||||
separator: nonBreakingSpace,
|
|
||||||
),
|
|
||||||
total: formatMoneyUiWithL10n(
|
|
||||||
l10n,
|
|
||||||
controller.aggregateDebitAmount,
|
|
||||||
separator: nonBreakingSpace,
|
|
||||||
),
|
),
|
||||||
|
total: formatMoneyUi(context, controller.aggregateDebitAmount),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ class UploadHistorySection extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ChangeNotifierProxyProvider<PaymentsProvider, RecentPaymentsController>(
|
return ChangeNotifierProxyProvider<
|
||||||
|
PaymentsProvider,
|
||||||
|
RecentPaymentsController
|
||||||
|
>(
|
||||||
create: (_) => RecentPaymentsController(),
|
create: (_) => RecentPaymentsController(),
|
||||||
update: (_, payments, controller) => controller!..update(payments),
|
update: (_, payments, controller) => controller!..update(payments),
|
||||||
child: const _RecentPaymentsView(),
|
child: const _RecentPaymentsView(),
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import 'package:pweb/pages/dashboard/payouts/summary/row.dart';
|
|||||||
|
|
||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
class PaymentFeeRow extends StatelessWidget {
|
class PaymentFeeRow extends StatelessWidget {
|
||||||
const PaymentFeeRow({super.key});
|
const PaymentFeeRow({super.key});
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ class PaymentFeeRow extends StatelessWidget {
|
|||||||
final l10 = AppLocalizations.of(context)!;
|
final l10 = AppLocalizations.of(context)!;
|
||||||
return PaymentSummaryRow(
|
return PaymentSummaryRow(
|
||||||
labelFactory: l10.fee,
|
labelFactory: l10.fee,
|
||||||
asset: fee,
|
money: fee,
|
||||||
value: fee == null ? l10.noFee : null,
|
value: fee == null ? l10.noFee : null,
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import 'package:pweb/pages/dashboard/payouts/summary/row.dart';
|
|||||||
|
|
||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
class PaymentRecipientReceivesRow extends StatelessWidget {
|
class PaymentRecipientReceivesRow extends StatelessWidget {
|
||||||
const PaymentRecipientReceivesRow({super.key});
|
const PaymentRecipientReceivesRow({super.key});
|
||||||
|
|
||||||
@@ -16,7 +15,7 @@ class PaymentRecipientReceivesRow extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) => Consumer<QuotationProvider>(
|
Widget build(BuildContext context) => Consumer<QuotationProvider>(
|
||||||
builder: (context, provider, _) => PaymentSummaryRow(
|
builder: (context, provider, _) => PaymentSummaryRow(
|
||||||
labelFactory: AppLocalizations.of(context)!.recipientWillReceive,
|
labelFactory: AppLocalizations.of(context)!.recipientWillReceive,
|
||||||
asset: provider.recipientGets,
|
money: provider.recipientGets,
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,27 +1,25 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:money2/money2.dart';
|
||||||
import 'package:pshared/models/asset.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/utils/money_display.dart';
|
import 'package:pweb/utils/money_display.dart';
|
||||||
|
|
||||||
|
|
||||||
class PaymentSummaryRow extends StatelessWidget {
|
class PaymentSummaryRow extends StatelessWidget {
|
||||||
final String Function(String) labelFactory;
|
final String Function(String) labelFactory;
|
||||||
final Asset? asset;
|
final Money? money;
|
||||||
final String? value;
|
final String? value;
|
||||||
final TextStyle? style;
|
final TextStyle? style;
|
||||||
|
|
||||||
const PaymentSummaryRow({
|
const PaymentSummaryRow({
|
||||||
super.key,
|
super.key,
|
||||||
required this.labelFactory,
|
required this.labelFactory,
|
||||||
required this.asset,
|
required this.money,
|
||||||
this.value,
|
this.value,
|
||||||
this.style,
|
this.style,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final formatted = value ?? formatAssetUi(context, asset);
|
final formatted = value ?? formatMoneyUi(context, money);
|
||||||
return Text(labelFactory(formatted), style: style);
|
return Text(labelFactory(formatted), style: style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import 'package:pweb/pages/dashboard/payouts/summary/row.dart';
|
|||||||
|
|
||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
class PaymentTotalRow extends StatelessWidget {
|
class PaymentTotalRow extends StatelessWidget {
|
||||||
const PaymentTotalRow({super.key});
|
const PaymentTotalRow({super.key});
|
||||||
|
|
||||||
@@ -16,8 +15,10 @@ class PaymentTotalRow extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) => Consumer<QuotationProvider>(
|
Widget build(BuildContext context) => Consumer<QuotationProvider>(
|
||||||
builder: (context, provider, _) => PaymentSummaryRow(
|
builder: (context, provider, _) => PaymentSummaryRow(
|
||||||
labelFactory: AppLocalizations.of(context)!.total,
|
labelFactory: AppLocalizations.of(context)!.total,
|
||||||
asset: provider.total,
|
money: provider.total,
|
||||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w600),
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w600),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user