From d7f295d37d30d9f51181ff39846376fab321427d Mon Sep 17 00:00:00 2001 From: Gerdriaan Mulder Date: Tue, 14 Jan 2020 22:58:32 +0100 Subject: [PATCH] PAIN structs ready for minimum required XML --- pain/group_header.go | 1 + pain/pain.go | 25 +---- pain/pain_regexp.go | 8 +- pain/payment_information.go | 11 +- pain/payment_information_transactions.go | 130 ++++++++++++++++++++++- 5 files changed, 145 insertions(+), 30 deletions(-) diff --git a/pain/group_header.go b/pain/group_header.go index f0d134e..c1bc1fd 100644 --- a/pain/group_header.go +++ b/pain/group_header.go @@ -2,6 +2,7 @@ package pain import ( "fmt" + "strings" ) type GrpHdr struct { diff --git a/pain/pain.go b/pain/pain.go index e1d4c35..bcd51f9 100644 --- a/pain/pain.go +++ b/pain/pain.go @@ -2,7 +2,6 @@ package pain import ( "fmt" - "regexp" "strings" ) @@ -31,30 +30,10 @@ func (p *PainXML) Valid() error { return nil } -type CdtrAgt struct { - InstitutionId FinInstnId `xml:"FinInstId"` -} - -type FinInstnId struct { - BIC string `xml:"BIC"` -} - -type CdtrSchmeId struct { - Id PartyIdSEPA3 `xml:"Id"` -} - -func (c *CdtrSchmeId) Valid() error { - return c.Id.Valid() -} - type CdtrAcct struct { Id IBAN `xml:"Id"` } -type IBAN struct { - IBAN string `xml:"IBAN"` -} - func (c *CdtrAcct) Valid() error { if e := c.Id.Valid(); e != nil { return fmt.Errorf("creditor account id not valid: %v", e) @@ -62,6 +41,10 @@ func (c *CdtrAcct) Valid() error { return nil } +type IBAN struct { + IBAN string `xml:"IBAN"` +} + func (i *IBAN) Valid() error { if !SEPARegexps["IBAN"].MatchString(i.IBAN) { return fmt.Errorf("IBAN does not match format") diff --git a/pain/pain_regexp.go b/pain/pain_regexp.go index 467fa7e..1c8cc3a 100644 --- a/pain/pain_regexp.go +++ b/pain/pain_regexp.go @@ -11,6 +11,7 @@ var ( Max15NumericText = regexp.MustCompile(`[0-9]{1,15}`) RestrictedDecimalNumber = regexp.MustCompile(`[+-]?\d+\.\d\d`) Max70Text = regexp.MustCompile(`.{1,70}`) + Max140Text = regexp.MustCompile(`.{1,140}`) ServiceLevelSEPACode = regexp.MustCompile(`SEPA`) SequenceType1Code = regexp.MustCompile(`FRST|RCUR|FNAL|OOFF`) @@ -27,11 +28,13 @@ var ( "BIC": BICIdentifier, "CreDtTm": ISODateTime, "CtrlSum": RestrictedDecimalNumber, - "EndToEndId": RestrictionIdentificationSEPA1, + "EndToEndId": RestrictedIdentificationSEPA1, "IBAN": IBAN2007Identifier, "Id": RestrictedPersonIdentifierSEPA, - "InstrId": RestrictionIdentificationSEPA1, + "InstrId": RestrictedIdentificationSEPA1, + "InstdAmt": RestrictedDecimalNumber, // Note: this is for the value "MsgId": RestrictedIdentificationSEPA1, + "MndtId": RestrictedIdentificationSEPA2, "NbOfTxs": Max15NumericText, "Nm": Max70Text, "PmtInfId": RestrictedIdentificationSEPA1, @@ -40,5 +43,6 @@ var ( "PmtMtd": PaymentMethod2Code, "ReqdColltnDt": ISODateTime, "SeqTp": SequenceType1Code, + "Ustrd": Max140Text, } ) diff --git a/pain/payment_information.go b/pain/payment_information.go index 6e24e9a..dabfe98 100644 --- a/pain/payment_information.go +++ b/pain/payment_information.go @@ -2,6 +2,7 @@ package pain import ( "fmt" + "strings" ) type PmtInf struct { @@ -56,8 +57,8 @@ type PmtTpInf struct { } type Cdtr struct { - Name string `xml:"Nm"` - PostalAddress PstlAddr `xml:"PstlAddr"` + Name string `xml:"Nm"` + PostalAddress PstlAdr `xml:"PstlAdr"` } func (c *Cdtr) Valid() error { @@ -76,12 +77,12 @@ func (c *Cdtr) Valid() error { } type AdrLine string -type PstlAddr struct { +type PstlAdr struct { Country string `xml:"Ctry"` AddressLines []AdrLine `xml:"AdrLine"` } -func (p *PstlAddr) Valid() error { +func (p *PstlAdr) Valid() error { var err []string if !SEPARegexps["Ctry"].MatchString(p.Country) { err = append(err, "country does not match format") @@ -107,7 +108,7 @@ type CdtrAgt struct { } func (c *CdtrAgt) Valid() error { - return c.InstituionId.Valid() + return c.InstitutionId.Valid() } type FinInstnId struct { diff --git a/pain/payment_information_transactions.go b/pain/payment_information_transactions.go index 7107973..74c98cd 100644 --- a/pain/payment_information_transactions.go +++ b/pain/payment_information_transactions.go @@ -6,8 +6,40 @@ import ( ) type DrctDbtTxInf struct { - Id PaymentId `xml:"PmtId"` - Amount CurrencyWithAmount `xml:"InstdAmt"` + Id PaymentId `xml:"PmtId"` + Amount CurrencyWithAmount `xml:"InstdAmt"` + Transaction DrctDbtTx `xml:"DrctDbtTx"` + Agent DbtrAgt `xml:"DbtrAgt"` + Debtor Dbtr `xml:"Dbtr"` + Account DbtrAcct `xml:"DbtrAcct"` + Info RmtInf `xml:"RmtInf"` +} + +func (d *DrctDbtTxInf) Valid() error { + var err []string + if e := d.Id.Valid(); e != nil { + err = append(err, fmt.Sprintf("%v", e)) + } + if e := d.Amount.Valid(); e != nil { + err = append(err, fmt.Sprintf("%v", e)) + } + if e := d.Transaction.Valid(); e != nil { + err = append(err, fmt.Sprintf("%v", e)) + } + if e := d.Agent.Valid(); e != nil { + err = append(err, fmt.Sprintf("%v", e)) + } + if e := d.Debtor.Valid(); e != nil { + err = append(err, fmt.Sprintf("%v", e)) + } + if e := d.Account.Valid(); e != nil { + err = append(err, fmt.Sprintf("%v", e)) + } + + if len(err) > 0 { + return fmt.Errorf("direct debit transaction information not valid: %v", strings.Join(err, ", ")) + } + return nil } type CurrencyWithAmount struct { @@ -15,6 +47,21 @@ type CurrencyWithAmount struct { Value string `xml:",innerxml"` } +func (c *CurrencyWithAmount) Valid() error { + var err []string + if c.Currency != "EUR" { + err = append(err, "expected Ccy EUR") + } + if !SEPARegexps["InstdAmnt"].MatchString(c.Value) { + err = append(err, "value does not match format") + } + + if len(err) > 0 { + return fmt.Errorf("currency with amount not valid: %v", strings.Join(err, ", ")) + } + return nil +} + type PaymentId struct { InstrumentId string `xml:"InstrId"` EndToEndId string `xml:"EndToEndId"` @@ -34,3 +81,82 @@ func (p *PaymentId) Valid() error { } return nil } + +type MndtRltdInf struct { + Id string `xml:"MndtId"` + SignatureDate string `xml:"DtOfSgntr"` + IsAmended bool `xml:"AmdmntInd"` + //AmdmntInfDtls is optional + //ElctrncSgntr is optional +} + +func (m *MndtRltdInf) Valid() error { + var err []string + if !SEPARegexps["MndtId"].MatchString(m.Id) { + err = append(err, "mandate id does not match format") + } + if !SEPARegexps["DtOfSgntr"].MatchString(m.SignatureDate) { + err = append(err, "date of signature does not match format") + } + + if len(err) > 0 { + return fmt.Errorf("mandate related information is not valid: %v", strings.Join(err, ", ")) + } + return nil +} + +type DrctDbtTx struct { + MandateRelatedInfo MndtRltdInf `xml:"MndtRltdInf"` + //CdtrSchemeId is optional +} + +func (d *DrctDbtTx) Valid() error { + return d.MandateRelatedInfo.Valid() +} + +type DbtrAgt struct { + InstitutionId FinInstnId `xml:"FinInstId"` +} + +func (d *DbtrAgt) Valid() error { + return d.InstitutionId.Valid() +} + +type Dbtr struct { + Name string `xml:"Nm"` + Address PstlAdr `xml:"PstlAdr"` +} + +func (d *Dbtr) Valid() error { + var err []string + if !SEPARegexps["Nm"].MatchString(d.Name) { + err = append(err, "name does not match format") + } + if e := d.Address.Valid(); e != nil { + err = append(err, fmt.Sprintf("%v", e)) + } + + if len(err) > 0 { + return fmt.Errorf("dbtr not valid: %v", strings.Join(err, ", ")) + } + return nil +} + +type DbtrAcct struct { + Id IBAN `xml:"IBAN"` +} + +func (d *DbtrAcct) Valid() error { + return d.Id.Valid() +} + +type RmtInf struct { + Value string `xml:"Ustrd"` +} + +func (r *RmtInf) Valid() error { + if !SEPARegexps["Ustrd"].MatchString(r.Value) { + return fmt.Errorf("remittance information does not match format") + } + return nil +}