Skip to content

Commit

Permalink
PaymentRequestInfoData to be slice instead of a union and remove Cur…
Browse files Browse the repository at this point in the history
…rency field (#68)

* feat: Changed PaymentRequestInfoData from a struct to a slice type ([]PaymentRequestInfoDataItem)
fix: PaymentProof is now a list of PaymentProofItem and can be constructed
feat: Added a test with mixed types of payment data and a test that showcase how to construct a payment data chore: Tests are running outside package.
  • Loading branch information
x1m3 authored Dec 6, 2024
1 parent d0efbb8 commit 9a7fa6e
Show file tree
Hide file tree
Showing 4 changed files with 342 additions and 154 deletions.
9 changes: 5 additions & 4 deletions .github/workflows/ci-lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: 1.21.0
- name: Checkout code
uses: actions/checkout@v3
go-version-file: go.mod
cache: true
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.54.2
version: v1.60.3
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
service:
golangci-lint-version: 1.45.x
golangci-lint-version: 1.60.3

run:
timeout: 2m
Expand Down
214 changes: 78 additions & 136 deletions protocol/payment.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"

"github.com/iden3/driver-did-iden3/pkg/document"
"github.com/iden3/go-schema-processor/v2/verifiable"
"github.com/pkg/errors"

Expand Down Expand Up @@ -34,9 +35,6 @@ const (

// Iden3PaymentRailsERC20V1Type is a Iden3PaymentRailsERC20V1 payment type
Iden3PaymentRailsERC20V1Type PaymentType = "Iden3PaymentRailsERC20V1"

// Eip712SignatureProofType is a EthereumEip712Signature2021 proof type
Eip712SignatureProofType ProofType = "EthereumEip712Signature2021"
)

// PaymentType is type for Payment
Expand All @@ -45,9 +43,6 @@ type PaymentType string
// PaymentRequestType is type for Payment request
type PaymentRequestType string

// ProofType is type for Proof
type ProofType string

// PaymentRequestMessage represents Iden3message for payment request.
type PaymentRequestMessage struct {
ID string `json:"id"`
Expand All @@ -72,147 +67,86 @@ type PaymentRequestMessageBody struct {

// PaymentRequestInfo represents the payments request information.
type PaymentRequestInfo struct {
Type string `json:"type,omitempty"`
Credentials []PaymentRequestInfoCredentials `json:"credentials"`
Description string `json:"description"`
Data PaymentRequestInfoData `json:"data"`
}

// PaymentRequestInfoData is a union type for field Data in PaymentRequestInfo.
// Only one of the fields can be set at a time.
type PaymentRequestInfoData struct {
dataType PaymentRequestType
crypto []Iden3PaymentRequestCryptoV1
rails []Iden3PaymentRailsRequestV1
railsERC20 []Iden3PaymentRailsERC20RequestV1
}

// NewPaymentRequestInfoDataCrypto creates a new PaymentRequestInfoData with Iden3PaymentRequestCryptoV1 data.
func NewPaymentRequestInfoDataCrypto(data Iden3PaymentRequestCryptoV1) PaymentRequestInfoData {
return PaymentRequestInfoData{
dataType: Iden3PaymentRequestCryptoV1Type,
crypto: []Iden3PaymentRequestCryptoV1{data},
}
}
type PaymentRequestInfoData []PaymentRequestInfoDataItem

// NewPaymentRequestInfoDataRails creates a new PaymentRequestInfoData with Iden3PaymentRailsRequestV1 data.
func NewPaymentRequestInfoDataRails(data Iden3PaymentRailsRequestV1) PaymentRequestInfoData {
return PaymentRequestInfoData{
dataType: Iden3PaymentRailsRequestV1Type,
rails: []Iden3PaymentRailsRequestV1{data},
}
}

// NewPaymentRequestInfoDataRailsERC20 creates a new PaymentRequestInfoData with Iden3PaymentRailsERC20RequestV1 data.
func NewPaymentRequestInfoDataRailsERC20(data Iden3PaymentRailsERC20RequestV1) PaymentRequestInfoData {
return PaymentRequestInfoData{
dataType: Iden3PaymentRailsERC20RequestV1Type,
railsERC20: []Iden3PaymentRailsERC20RequestV1{data},
}
}

// Type returns the type of the data in the union. You can use Data() to get the data.
func (p *PaymentRequestInfoData) Type() PaymentRequestType {
return p.dataType
}

// Data returns the data in the union. You can use Type() to determine the type of the data.
func (p *PaymentRequestInfoData) Data() interface{} {
switch p.dataType {
case Iden3PaymentRequestCryptoV1Type:
return p.crypto
case Iden3PaymentRailsRequestV1Type:
return p.rails
case Iden3PaymentRailsERC20RequestV1Type:
return p.railsERC20
}
return nil
// PaymentRequestInfoDataItem is the interface that any PaymentRequestInfoData.Data item must implement.
type PaymentRequestInfoDataItem interface {
PaymentRequestType() PaymentRequestType
}

// MarshalJSON marshals the PaymentRequestInfoData into JSON.
func (p PaymentRequestInfoData) MarshalJSON() ([]byte, error) {
switch p.dataType {
case Iden3PaymentRequestCryptoV1Type:
return json.Marshal(p.crypto[0])
case Iden3PaymentRailsRequestV1Type:
return json.Marshal(p.rails)
case Iden3PaymentRailsERC20RequestV1Type:
return json.Marshal(p.railsERC20)
if len(p) == 1 && p[0].PaymentRequestType() == Iden3PaymentRequestCryptoV1Type {
return json.Marshal(p[0])
}
return nil, errors.New("failed to marshal not initialized PaymentRequestInfoData")
return json.Marshal([]PaymentRequestInfoDataItem(p))
}

// UnmarshalJSON unmarshal the PaymentRequestInfoData from JSON.
func (p *PaymentRequestInfoData) UnmarshalJSON(data []byte) error {
var item struct {
Type PaymentRequestType `json:"type"`
}
var collection []struct {
type rawItem struct {
Type PaymentRequestType `json:"type"`
}

p.crypto, p.rails, p.railsERC20 = nil, nil, nil
var item rawItem
var collection []json.RawMessage

if err := json.Unmarshal(data, &item); err == nil {
p.dataType = item.Type
return p.unmarshalFromItem(item.Type, data)
err := json.Unmarshal(data, &item)
if err == nil {
o, errItem := p.unmarshalFromItem(item.Type, data)
if errItem != nil {
return errItem
}
*p = append(*p, o)
return nil
}

if err := json.Unmarshal(data, &collection); err == nil {
if len(collection) == 0 {
return nil
err = json.Unmarshal(data, &collection)
if err != nil {
return fmt.Errorf("PaymentRequestInfoData must be a PaymentRequestInfoDataItem or a collection: %w", err)
}
for n, rawItem := range collection {
if err := json.Unmarshal(rawItem, &item); err != nil {
return fmt.Errorf("field PaymentRequestInfoData[%d].Type not found: %w", n, err)
}

p.dataType = collection[0].Type
return p.unmarshalFromCollection(p.dataType, data)
o, err := p.unmarshalFromItem(item.Type, rawItem)
if err != nil {
return err
}
*p = append(*p, o)
}
return errors.New("failed to unmarshal PaymentRequestInfoData. not a single item nor a collection")
return nil
}

func (p *PaymentRequestInfoData) unmarshalFromItem(typ PaymentRequestType, data []byte) error {
func (p *PaymentRequestInfoData) unmarshalFromItem(typ PaymentRequestType, data []byte) (PaymentRequestInfoDataItem, error) {
switch typ {
case Iden3PaymentRequestCryptoV1Type:
var o Iden3PaymentRequestCryptoV1
if err := json.Unmarshal(data, &o); err != nil {
return fmt.Errorf("unmarshalling PaymentRequestInfoData: %w", err)
return nil, fmt.Errorf("unmarshalling PaymentRequestInfoData: %w", err)
}
p.crypto = []Iden3PaymentRequestCryptoV1{o}
return o, nil
case Iden3PaymentRailsRequestV1Type:
var o Iden3PaymentRailsRequestV1
if err := json.Unmarshal(data, &o); err != nil {
return fmt.Errorf("unmarshalling PaymentRequestInfoData: %w", err)
return nil, fmt.Errorf("unmarshalling PaymentRequestInfoData: %w", err)
}
p.rails = []Iden3PaymentRailsRequestV1{o}
return o, nil
case Iden3PaymentRailsERC20RequestV1Type:
var o Iden3PaymentRailsERC20RequestV1
if err := json.Unmarshal(data, &o); err != nil {
return fmt.Errorf("unmarshalling PaymentRequestInfoData: %w", err)
}
p.railsERC20 = []Iden3PaymentRailsERC20RequestV1{o}
default:
return errors.Errorf("unmarshalling PaymentRequestInfoData. unknown type: %s", typ)
}
return nil
}

func (p *PaymentRequestInfoData) unmarshalFromCollection(typ PaymentRequestType, data []byte) error {
switch typ {
case Iden3PaymentRequestCryptoV1Type:
if err := json.Unmarshal(data, &p.crypto); err != nil {
return fmt.Errorf("unmarshalling PaymentRequestInfoData: %w", err)
}
case Iden3PaymentRailsRequestV1Type:
if err := json.Unmarshal(data, &p.rails); err != nil {
return fmt.Errorf("unmarshalling PaymentRequestInfoData: %w", err)
}
case Iden3PaymentRailsERC20RequestV1Type:
if err := json.Unmarshal(data, &p.railsERC20); err != nil {
return fmt.Errorf("unmarshalling PaymentRequestInfoData: %w", err)
return nil, fmt.Errorf("unmarshalling PaymentRequestInfoData: %w", err)
}
return o, nil
default:
return errors.Errorf("unmarshalling PaymentRequestInfoData. unknown type: %s", typ)
return nil, errors.Errorf("unmarshalling PaymentRequestInfoData. unknown type: %s", typ)
}
return nil
}

// Iden3PaymentRequestCryptoV1 represents the Iden3PaymentRequestCryptoV1 payment request data.
Expand All @@ -227,6 +161,11 @@ type Iden3PaymentRequestCryptoV1 struct {
Expiration string `json:"expiration,omitempty"`
}

// PaymentRequestType implements the PaymentRequestInfoDataItem interface.
func (i Iden3PaymentRequestCryptoV1) PaymentRequestType() PaymentRequestType {
return Iden3PaymentRequestCryptoV1Type
}

// Iden3PaymentRailsRequestV1 represents the Iden3PaymentRailsRequestV1 payment request data.
type Iden3PaymentRailsRequestV1 struct {
Nonce string `json:"nonce"`
Expand All @@ -237,7 +176,11 @@ type Iden3PaymentRailsRequestV1 struct {
ExpirationDate string `json:"expirationDate"`
Proof PaymentProof `json:"proof"`
Metadata string `json:"metadata"`
Currency string `json:"currency"`
}

// PaymentRequestType implements the PaymentRequestInfoDataItem interface.
func (i Iden3PaymentRailsRequestV1) PaymentRequestType() PaymentRequestType {
return Iden3PaymentRailsRequestV1Type
}

// Iden3PaymentRailsERC20RequestV1 represents the Iden3PaymentRailsERC20RequestV1 payment request data.
Expand All @@ -250,49 +193,45 @@ type Iden3PaymentRailsERC20RequestV1 struct {
ExpirationDate string `json:"expirationDate"`
Proof PaymentProof `json:"proof"`
Metadata string `json:"metadata"`
Currency string `json:"currency"`
TokenAddress string `json:"tokenAddress"`
Features []PaymentFeatures `json:"features,omitempty"`
}

// PaymentRequestType implements the PaymentRequestInfoDataItem interface.
func (i Iden3PaymentRailsERC20RequestV1) PaymentRequestType() PaymentRequestType {
return Iden3PaymentRailsERC20RequestV1Type
}

// PaymentFeatures represents type Features used in ERC20 payment request.
type PaymentFeatures string

// PaymentProof represents a payment proof.
type PaymentProof struct {
dataType ProofType
eip712Signature []EthereumEip712Signature2021
}
type PaymentProof []PaymentProofItem

// NewPaymentProofEip712Signature creates a new PaymentProof with EthereumEip712Signature2021 data.
func NewPaymentProofEip712Signature(data []EthereumEip712Signature2021) PaymentProof {
return PaymentProof{
dataType: Eip712SignatureProofType,
eip712Signature: data,
}
// PaymentProofItem is the interface that any PaymentProof item must implement.
type PaymentProofItem interface {
PaymentProofItem() verifiable.ProofType
}

// UnmarshalJSON unmarshal the PaymentRequestInfoData from JSON.
func (p *PaymentProof) UnmarshalJSON(data []byte) error {
p.dataType = Eip712SignatureProofType
var col []EthereumEip712Signature2021
var col []*EthereumEip712Signature2021
if err := json.Unmarshal(data, &col); err != nil {
var single EthereumEip712Signature2021
if err := json.Unmarshal(data, &single); err != nil {
return fmt.Errorf("failed to unmarshal EthereumEip712Signature2021Col: %w", err)
}
col = append(col, single)
col = append(col, &single)
}
for _, item := range col {
*p = append(*p, *item)
}
p.eip712Signature = col
return nil
}

// MarshalJSON marshals the PaymentProof into JSON.
func (p PaymentProof) MarshalJSON() ([]byte, error) {
if p.dataType == Eip712SignatureProofType {
return json.Marshal(p.eip712Signature)
}
return nil, errors.New("failed to marshal not initialized PaymentProof")
return json.Marshal([]PaymentProofItem(p))
}

// EthereumEip712Signature2021 represents the Ethereum EIP712 signature.
Expand All @@ -305,6 +244,11 @@ type EthereumEip712Signature2021 struct {
Eip712 Eip712Data `json:"eip712"`
}

// PaymentProofItem implements the PaymentProofItem interface.
func (e EthereumEip712Signature2021) PaymentProofItem() verifiable.ProofType {
return document.EthereumEip712SignatureProof2021Type
}

// Eip712Data represents the EIP712 data.
type Eip712Data struct {
Types string `json:"types"`
Expand Down Expand Up @@ -372,8 +316,8 @@ func NewPaymentRails(data Iden3PaymentRailsV1) Payment {
}
}

// NEwPaymentRailsERC20 creates a new Payment with Iden3PaymentRailsERC20V1 data.
func NEwPaymentRailsERC20(data Iden3PaymentRailsERC20V1) Payment {
// NewPaymentRailsERC20 creates a new Payment with Iden3PaymentRailsERC20V1 data.
func NewPaymentRailsERC20(data Iden3PaymentRailsERC20V1) Payment {
return Payment{
dataType: Iden3PaymentRailsERC20V1Type,
railsERC: &data,
Expand Down Expand Up @@ -473,17 +417,15 @@ type PaymentContext struct {
}

// NewPaymentContextString creates a new PaymentContext with a string.
func NewPaymentContextString(str string) PaymentContext {
return PaymentContext{str: &str}
}

// NewPaymentContextStringCol creates a new PaymentContext with a string collection.
func NewPaymentContextStringCol(strCol []string) PaymentContext {
return PaymentContext{strCol: strCol}
func NewPaymentContextString(str ...string) PaymentContext {
if len(str) == 1 {
return PaymentContext{str: &str[0]}
}
return PaymentContext{strCol: str}
}

// NewPaymentContextItemCol creates a new PaymentContext with an interface{} collection.
func NewPaymentContextItemCol(itemCol []interface{}) PaymentContext {
func NewPaymentContextItemCol(itemCol ...interface{}) PaymentContext {
return PaymentContext{itemCol: itemCol}
}

Expand Down
Loading

0 comments on commit 9a7fa6e

Please sign in to comment.