Compare commits
5 Commits
ac83bb06dd
...
4bbde4c542
| Author | SHA1 | Date |
|---|---|---|
|
|
4bbde4c542 | 6 years ago |
|
|
2e662f09af | 6 years ago |
|
|
ca976d67c0 | 6 years ago |
|
|
ccc2d1efa8 | 6 years ago |
|
|
da5db64883 | 6 years ago |
7 changed files with 405 additions and 247 deletions
@ -1,226 +0,0 @@ |
|||||
package sepa |
|
||||
|
|
||||
import ( |
|
||||
"fmt" |
|
||||
"regexp" |
|
||||
"strings" |
|
||||
) |
|
||||
|
|
||||
var ( |
|
||||
SEPARegexps = map[string]*regexp.Regexp{ |
|
||||
"MsgId": RestrictedIdentificationSEPA1, |
|
||||
"CreDtTm": ISODateTime, |
|
||||
"ReqdColltnDt": ISODateTime, |
|
||||
"NbOfTxs": Max15NumericText, |
|
||||
"CtrlSum": RestrictedDecimalNumber, |
|
||||
"Nm": Max70Text, |
|
||||
"PmtInfId": RestrictedIdentificationSEPA1, |
|
||||
"PmtMtd": PaymentMethod2Code, |
|
||||
"PmtMetaSvcLvl": ServiceLevelSEPACode, |
|
||||
"PmtMetaLclInstrm": LocalInstrumentSEPACode, |
|
||||
"SeqTp": SequenceType1Code, |
|
||||
"IBAN": IBAN2007Identifier, |
|
||||
} |
|
||||
) |
|
||||
|
|
||||
type PainXML struct { |
|
||||
GroupHeader GrpHdr `xml:"GrpHdr"` |
|
||||
PaymentInformation []PmtInf `xml:"PmtInf"` |
|
||||
TransactionInformation []TxInf `xml:"DrctDbtTxInf"` |
|
||||
} |
|
||||
|
|
||||
func (p *PainXML) Valid() error { |
|
||||
var err []string |
|
||||
if e := p.GroupHeader.Valid(); e != nil { |
|
||||
err = append(err, fmt.Sprintf("%v", e)) |
|
||||
} |
|
||||
if len(p.PaymentInformation) < 1 { |
|
||||
err = append(err, "no payment information") |
|
||||
} |
|
||||
if len(p.TransactionInformation) < 1 { |
|
||||
err = append(err, "no transaction information") |
|
||||
} |
|
||||
|
|
||||
if len(err) > 0 { |
|
||||
return fmt.Errorf("pain XML not valid: %v", strings.Join(err, ", ")) |
|
||||
} |
|
||||
|
|
||||
return nil |
|
||||
} |
|
||||
|
|
||||
type GrpHdr struct { |
|
||||
MessageId string `xml:"MsgId"` |
|
||||
Timestamp string `xml:"CreDtTm"` |
|
||||
NumTx string `xml:"NbOfTxs"` |
|
||||
Sum string `xml:"CtrlSum"` |
|
||||
InitiatingParty InitgPty `xml:"InitgPty"` |
|
||||
} |
|
||||
|
|
||||
func (g *GrpHdr) Valid() error { |
|
||||
var err []string |
|
||||
if !SEPARegexps["MsgId"].MatchString(g.MessageId) { |
|
||||
err = append(err, "msgId did not match expected format") |
|
||||
} |
|
||||
if !SEPARegexps["CreDtTm"].MatchString(g.Timestamp) { |
|
||||
err = append(err, "timestamp did not match expected format") |
|
||||
} |
|
||||
if !SEPARegexps["NbOfTxs"].MatchString(g.NumTx) { |
|
||||
err = append(err, "numtx did not match expected format") |
|
||||
} |
|
||||
if !SEPARegexps["CtrlSum"].MatchString(g.Sum) { |
|
||||
err = append(err, "sum did not match expected format") |
|
||||
} |
|
||||
// Note: we assume g.InitiatingParty is initialized.
|
|
||||
if !SEPARegexps["Nm"].MatchString(g.InitiatingParty.Name) { |
|
||||
err = append(err, "initiating party's name did not match expected format") |
|
||||
} |
|
||||
|
|
||||
if len(err) > 0 { |
|
||||
return fmt.Errorf("group header not valid: %v", strings.Join(err, ", ")) |
|
||||
} |
|
||||
return nil |
|
||||
} |
|
||||
|
|
||||
type PmtInf struct { |
|
||||
Id string `xml:"PmtInfId"` |
|
||||
Method string `xml:"PmtMtd"` |
|
||||
PaymentMeta PmtTpInf `xml:"PmtTpInf"` |
|
||||
CollectionDate string `xml:"ReqdColltnDt"` |
|
||||
Creditor Cdtr `xml:"Cdtr"` |
|
||||
CreditorAccount CdtrAcct `xml:"CdtrAcct"` |
|
||||
} |
|
||||
|
|
||||
func (pmt *PmtInf) Valid() error { |
|
||||
var err []string |
|
||||
if !SEPARegexps["PmtInfId"].MatchString(pmt.Id) { |
|
||||
err = append(err, "payment info id does not match expected format") |
|
||||
} |
|
||||
if !SEPARegexps["PmtMtd"].MatchString(pmt.Method) { |
|
||||
err = append(err, "payment method does not match expected format") |
|
||||
} |
|
||||
if e := pmt.PaymentMeta.Valid(); e != nil { |
|
||||
err = append(err, fmt.Sprintf("payment meta not valid: %v", e)) |
|
||||
} |
|
||||
if !SEPARegexps["ReqdColltnDt"].MatchString(pmt.CollectionDate) { |
|
||||
err = append(err, "payment collection date does not match expected format") |
|
||||
} |
|
||||
if e := pmt.Creditor.Valid(); e != nil { |
|
||||
err = append(err, fmt.Sprintf("payment creditor not valid: %v", e)) |
|
||||
} |
|
||||
if e := pmt.CreditorAccount.Valid(); e != nil { |
|
||||
err = append(err, fmt.Sprintf("payment creditor account not valid: %v", e)) |
|
||||
} |
|
||||
|
|
||||
if len(err) > 0 { |
|
||||
return fmt.Errorf("payment info (Id: %q) not valid: %v", pmt.Id, strings.Join(err, ", ")) |
|
||||
} |
|
||||
return nil |
|
||||
} |
|
||||
|
|
||||
type PmtTpInf struct { |
|
||||
ServiceLevel Code `xml:"SvcLvl"` |
|
||||
LocalInstrument Code `xml:"LclIntrm"` |
|
||||
SequenceType string `xml:"SeqTp"` |
|
||||
} |
|
||||
|
|
||||
type Cdtr struct { |
|
||||
Name string `xml:"Nm"` |
|
||||
PostalAddress PstlAddr `xml:"PstlAddr"` |
|
||||
} |
|
||||
|
|
||||
func (c *Cdtr) Valid() error { |
|
||||
var err []string |
|
||||
if !SEPARegexps["Nm"].MatchString(c.Name) { |
|
||||
err = append(err, "creditor name does not match format") |
|
||||
} |
|
||||
if e := c.PostalAddress.Valid(); e != nil { |
|
||||
err = append(err, fmt.Sprintf("creditor postal address not valid: %v", e)) |
|
||||
} |
|
||||
|
|
||||
if len(err) > 0 { |
|
||||
return fmt.Errorf("creditor (Nm: %q) not valid: %v", c.Name, strings.Join(err, ", ")) |
|
||||
} |
|
||||
return nil |
|
||||
} |
|
||||
|
|
||||
type PstlAddr struct { |
|
||||
Country string `xml:"Ctry"` |
|
||||
AddressLines []AdrLine `xml:"AdrLine"` |
|
||||
} |
|
||||
|
|
||||
func (p *PstlAddr) Valid() error { |
|
||||
var err []string |
|
||||
if !SEPARegexps["Ctry"].MatchString(p.Country) { |
|
||||
err = append(err, "country does not match format") |
|
||||
} |
|
||||
if len(p.AddressLines) > 2 { |
|
||||
err = append(err, "expected at most 2 address lines") |
|
||||
} |
|
||||
for i, line := range p.AddressLines { |
|
||||
if !SEPARegexps["AdrLine"].MatchString(string(line)) { |
|
||||
err = append(err, fmt.Sprintf("address line %d (%q) does not match format", i, line)) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if len(err) > 0 { |
|
||||
return fmt.Errorf("address not valid: %v", strings.Join(err, ", ")) |
|
||||
} |
|
||||
|
|
||||
return nil |
|
||||
} |
|
||||
|
|
||||
type AdrLine string |
|
||||
|
|
||||
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) |
|
||||
} |
|
||||
return nil |
|
||||
} |
|
||||
|
|
||||
func (i *IBAN) Valid() error { |
|
||||
if !SEPARegexps["IBAN"].MatchString(i.IBAN) { |
|
||||
return fmt.Errorf("IBAN does not match format") |
|
||||
} |
|
||||
return nil |
|
||||
} |
|
||||
|
|
||||
type Code struct { |
|
||||
Code string `xml:"Cd"` |
|
||||
} |
|
||||
|
|
||||
func (meta *PmtTpInf) Valid() error { |
|
||||
var err []string |
|
||||
// TODO: check meta.ServiceLevel for nil
|
|
||||
if !SEPARegexps["PmtMetaSvcLvl"].MatchString(meta.ServiceLevel.Code) { |
|
||||
err = append(err, "incorrect service level present (must be 'SEPA')") |
|
||||
} |
|
||||
// TODO: check meta.LocalInstrument for nil
|
|
||||
if !SEPARegexps["PmtMetaLclInstrm"].MatchString(meta.LocalInstrument.Code) { |
|
||||
err = append(err, "incorrect local instrument present") |
|
||||
} |
|
||||
if !SEPARegexps["SeqTp"].MatchString(meta.SequenceType) { |
|
||||
err = append(err, "sequence type has incorrect format") |
|
||||
} |
|
||||
|
|
||||
if len(err) > 0 { |
|
||||
return fmt.Errorf("payment meta not valid: %v", strings.Join(err, ", ")) |
|
||||
} |
|
||||
return nil |
|
||||
} |
|
||||
|
|
||||
type TxInf struct { |
|
||||
} |
|
||||
|
|
||||
type InitgPty struct { |
|
||||
Name string `xml:"Nm"` |
|
||||
// We do not implement the "Id" element from "PartyIdentificationSEPA1"
|
|
||||
} |
|
||||
@ -0,0 +1,38 @@ |
|||||
|
package pain |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
) |
||||
|
|
||||
|
type GrpHdr struct { |
||||
|
MessageId string `xml:"MsgId"` |
||||
|
Timestamp string `xml:"CreDtTm"` |
||||
|
NumTx string `xml:"NbOfTxs"` |
||||
|
Sum string `xml:"CtrlSum"` |
||||
|
InitiatingParty PartyIdSEPA1 `xml:"InitgPty"` |
||||
|
} |
||||
|
|
||||
|
func (g *GrpHdr) Valid() error { |
||||
|
var err []string |
||||
|
if !SEPARegexps["MsgId"].MatchString(g.MessageId) { |
||||
|
err = append(err, "msgId did not match expected format") |
||||
|
} |
||||
|
if !SEPARegexps["CreDtTm"].MatchString(g.Timestamp) { |
||||
|
err = append(err, "timestamp did not match expected format") |
||||
|
} |
||||
|
if !SEPARegexps["NbOfTxs"].MatchString(g.NumTx) { |
||||
|
err = append(err, "numtx did not match expected format") |
||||
|
} |
||||
|
if !SEPARegexps["CtrlSum"].MatchString(g.Sum) { |
||||
|
err = append(err, "sum did not match expected format") |
||||
|
} |
||||
|
// Note: we assume g.InitiatingParty is initialized.
|
||||
|
if !SEPARegexps["Nm"].MatchString(g.InitiatingParty.Name) { |
||||
|
err = append(err, "initiating party's name did not match expected format") |
||||
|
} |
||||
|
|
||||
|
if len(err) > 0 { |
||||
|
return fmt.Errorf("group header not valid: %v", strings.Join(err, ", ")) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
@ -0,0 +1,157 @@ |
|||||
|
package pain |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"regexp" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
type PainXML struct { |
||||
|
GroupHeader GrpHdr `xml:"GrpHdr"` |
||||
|
PaymentInformation []PmtInf `xml:"PmtInf"` |
||||
|
TransactionInformation []TxInf `xml:"DrctDbtTxInf"` |
||||
|
} |
||||
|
|
||||
|
func (p *PainXML) Valid() error { |
||||
|
var err []string |
||||
|
if e := p.GroupHeader.Valid(); e != nil { |
||||
|
err = append(err, fmt.Sprintf("%v", e)) |
||||
|
} |
||||
|
if len(p.PaymentInformation) < 1 { |
||||
|
err = append(err, "no payment information") |
||||
|
} |
||||
|
if len(p.TransactionInformation) < 1 { |
||||
|
err = append(err, "no transaction information") |
||||
|
} |
||||
|
|
||||
|
if len(err) > 0 { |
||||
|
return fmt.Errorf("pain XML not valid: %v", strings.Join(err, ", ")) |
||||
|
} |
||||
|
|
||||
|
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) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (i *IBAN) Valid() error { |
||||
|
if !SEPARegexps["IBAN"].MatchString(i.IBAN) { |
||||
|
return fmt.Errorf("IBAN does not match format") |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
type Code struct { |
||||
|
Code string `xml:"Cd"` |
||||
|
} |
||||
|
|
||||
|
func (meta *PmtTpInf) Valid() error { |
||||
|
var err []string |
||||
|
// TODO: check meta.ServiceLevel for nil
|
||||
|
if !SEPARegexps["PmtMetaSvcLvl"].MatchString(meta.ServiceLevel.Code) { |
||||
|
err = append(err, "incorrect service level present (must be 'SEPA')") |
||||
|
} |
||||
|
// TODO: check meta.LocalInstrument for nil
|
||||
|
if !SEPARegexps["PmtMetaLclInstrm"].MatchString(meta.LocalInstrument.Code) { |
||||
|
err = append(err, "incorrect local instrument present") |
||||
|
} |
||||
|
if !SEPARegexps["SeqTp"].MatchString(meta.SequenceType) { |
||||
|
err = append(err, "sequence type has incorrect format") |
||||
|
} |
||||
|
|
||||
|
if len(err) > 0 { |
||||
|
return fmt.Errorf("payment meta not valid: %v", strings.Join(err, ", ")) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
type TxInf struct { |
||||
|
} |
||||
|
|
||||
|
type PartySEPA2 struct { |
||||
|
PrivateId PersonIdSEPA2 `xml:"PrvtId"` |
||||
|
} |
||||
|
|
||||
|
func (p *PartySEPA2) Valid() error { |
||||
|
return p.PrivateId.Valid() |
||||
|
} |
||||
|
|
||||
|
type PartyIdSEPA1 struct { |
||||
|
Name string `xml:"Nm"` |
||||
|
// We do not implement the "Id" element from "PartyIdentificationSEPA1"
|
||||
|
} |
||||
|
|
||||
|
type PartyIdSEPA3 struct { |
||||
|
Id PartySEPA2 `xml:"Id"` |
||||
|
} |
||||
|
|
||||
|
func (p *PartyIdSEPA3) Valid() error { |
||||
|
return p.Id.Valid() |
||||
|
} |
||||
|
|
||||
|
type PersonIdSEPA2 struct { |
||||
|
Other RestrictedPersonIdSEPA `xml:"Othr"` |
||||
|
} |
||||
|
|
||||
|
func (p *PersonIdSEPA2) Valid() error { |
||||
|
return p.Other.Valid() |
||||
|
} |
||||
|
|
||||
|
type RestrictedPersonIdSEPA struct { |
||||
|
Id string `xml:"Id"` // RestrictedPersonIdentifierSEPA
|
||||
|
SchemeName RestrictedPersonIdSchemeNameSEPA `xml:"SchmeNm"` |
||||
|
} |
||||
|
|
||||
|
func (r *RestrictedPersonIdSEPA) Valid() error { |
||||
|
var err []string |
||||
|
if !SEPARegexps["Id"].MatchString(r.Id) { |
||||
|
err = append(err, "id of RestrictedPersonIdSEPA does not match format") |
||||
|
} |
||||
|
if e := r.SchemeName.Valid(); e != nil { |
||||
|
err = append(err, fmt.Sprintf("SchemeName not valid: %v", e)) |
||||
|
} |
||||
|
|
||||
|
if len(err) > 0 { |
||||
|
return fmt.Errorf("restricted person id SEPA not valid: %v", strings.Join(err, ", ")) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
type RestrictedPersonIdSchemeNameSEPA struct { |
||||
|
Party string `xml:"Prty"` // IdentificationSchemeNameSEPA
|
||||
|
} |
||||
|
|
||||
|
func (r *RestrictedPersonIdSchemeNameSEPA) Valid() error { |
||||
|
if r.Party != "SEPA" { |
||||
|
return fmt.Errorf("party should be 'SEPA', got %v", r.Party) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
@ -0,0 +1,44 @@ |
|||||
|
package pain |
||||
|
|
||||
|
import ( |
||||
|
"regexp" |
||||
|
) |
||||
|
|
||||
|
var ( |
||||
|
ISODateTime = regexp.MustCompile(`\d{4}(-\d\d){2}T\d\d(:\d\d){2}`) |
||||
|
RestrictedIdentificationSEPA1 = regexp.MustCompile(`([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|'| ]){1,35}`) |
||||
|
RestrictedIdentificationSEPA2 = regexp.MustCompile(`([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){1,35}`) |
||||
|
Max15NumericText = regexp.MustCompile(`[0-9]{1,15}`) |
||||
|
RestrictedDecimalNumber = regexp.MustCompile(`[+-]?\d+\.\d\d`) |
||||
|
Max70Text = regexp.MustCompile(`.{1,70}`) |
||||
|
|
||||
|
ServiceLevelSEPACode = regexp.MustCompile(`SEPA`) |
||||
|
SequenceType1Code = regexp.MustCompile(`FRST|RCUR|FNAL|OOFF`) |
||||
|
PaymentMethod2Code = regexp.MustCompile(`DD`) |
||||
|
LocalInstrumentSEPACode = regexp.MustCompile(`CORE|B2B`) |
||||
|
|
||||
|
IBAN2007Identifier = regexp.MustCompile(`[A-Z]{2,2}[0-9]{2,2}[a-zA-Z0-9]{1,30}`) |
||||
|
RestrictedPersonIdentifierSEPA = regexp.MustCompile(`[a-zA-Z]{2,2}[0-9]{2,2}([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){3,3}([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){1,28}`) |
||||
|
BICIdentifier = regexp.MustCompile(`[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}`) |
||||
|
) |
||||
|
|
||||
|
var ( |
||||
|
SEPARegexps = map[string]*regexp.Regexp{ |
||||
|
"BIC": BICIdentifier, |
||||
|
"CreDtTm": ISODateTime, |
||||
|
"CtrlSum": RestrictedDecimalNumber, |
||||
|
"EndToEndId": RestrictionIdentificationSEPA1, |
||||
|
"IBAN": IBAN2007Identifier, |
||||
|
"Id": RestrictedPersonIdentifierSEPA, |
||||
|
"InstrId": RestrictionIdentificationSEPA1, |
||||
|
"MsgId": RestrictedIdentificationSEPA1, |
||||
|
"NbOfTxs": Max15NumericText, |
||||
|
"Nm": Max70Text, |
||||
|
"PmtInfId": RestrictedIdentificationSEPA1, |
||||
|
"PmtMetaLclInstrm": LocalInstrumentSEPACode, |
||||
|
"PmtMetaSvcLvl": ServiceLevelSEPACode, |
||||
|
"PmtMtd": PaymentMethod2Code, |
||||
|
"ReqdColltnDt": ISODateTime, |
||||
|
"SeqTp": SequenceType1Code, |
||||
|
} |
||||
|
) |
||||
@ -0,0 +1,130 @@ |
|||||
|
package pain |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
) |
||||
|
|
||||
|
type PmtInf struct { |
||||
|
Id string `xml:"PmtInfId"` |
||||
|
Method string `xml:"PmtMtd"` |
||||
|
PaymentMeta PmtTpInf `xml:"PmtTpInf"` |
||||
|
CollectionDate string `xml:"ReqdColltnDt"` |
||||
|
Creditor Cdtr `xml:"Cdtr"` |
||||
|
CreditorAccount CdtrAcct `xml:"CdtrAcct"` |
||||
|
CreditorAgent CdtrAgt `xml:"CdtrAgt"` |
||||
|
SchemeId CdtrSchmeId `xml:"CdtrSchmeId"` |
||||
|
Transactions []DrctDbtTxInf `xml:"DrctDbtTxInf"` |
||||
|
} |
||||
|
|
||||
|
func (pmt *PmtInf) Valid() error { |
||||
|
var err []string |
||||
|
if !SEPARegexps["PmtInfId"].MatchString(pmt.Id) { |
||||
|
err = append(err, "payment info id does not match expected format") |
||||
|
} |
||||
|
if !SEPARegexps["PmtMtd"].MatchString(pmt.Method) { |
||||
|
err = append(err, "payment method does not match expected format") |
||||
|
} |
||||
|
if e := pmt.PaymentMeta.Valid(); e != nil { |
||||
|
err = append(err, fmt.Sprintf("payment meta not valid: %v", e)) |
||||
|
} |
||||
|
if !SEPARegexps["ReqdColltnDt"].MatchString(pmt.CollectionDate) { |
||||
|
err = append(err, "payment collection date does not match expected format") |
||||
|
} |
||||
|
if e := pmt.Creditor.Valid(); e != nil { |
||||
|
err = append(err, fmt.Sprintf("payment creditor not valid: %v", e)) |
||||
|
} |
||||
|
if e := pmt.CreditorAccount.Valid(); e != nil { |
||||
|
err = append(err, fmt.Sprintf("payment creditor account not valid: %v", e)) |
||||
|
} |
||||
|
if e := pmt.CreditorAgent.Valid(); e != nil { |
||||
|
err = append(err, fmt.Sprintf("payment creditor agent not valid: %v", e)) |
||||
|
} |
||||
|
if e := pmt.SchemeId.Valid(); e != nil { |
||||
|
err = append(err, fmt.Sprintf("payment scheme id not valid: %v", e)) |
||||
|
} |
||||
|
|
||||
|
if len(err) > 0 { |
||||
|
return fmt.Errorf("payment info (Id: %q) not valid: %v", pmt.Id, strings.Join(err, ", ")) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
type PmtTpInf struct { |
||||
|
ServiceLevel Code `xml:"SvcLvl"` |
||||
|
LocalInstrument Code `xml:"LclIntrm"` |
||||
|
SequenceType string `xml:"SeqTp"` |
||||
|
} |
||||
|
|
||||
|
type Cdtr struct { |
||||
|
Name string `xml:"Nm"` |
||||
|
PostalAddress PstlAddr `xml:"PstlAddr"` |
||||
|
} |
||||
|
|
||||
|
func (c *Cdtr) Valid() error { |
||||
|
var err []string |
||||
|
if !SEPARegexps["Nm"].MatchString(c.Name) { |
||||
|
err = append(err, "creditor name does not match format") |
||||
|
} |
||||
|
if e := c.PostalAddress.Valid(); e != nil { |
||||
|
err = append(err, fmt.Sprintf("creditor postal address not valid: %v", e)) |
||||
|
} |
||||
|
|
||||
|
if len(err) > 0 { |
||||
|
return fmt.Errorf("creditor (Nm: %q) not valid: %v", c.Name, strings.Join(err, ", ")) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
type AdrLine string |
||||
|
type PstlAddr struct { |
||||
|
Country string `xml:"Ctry"` |
||||
|
AddressLines []AdrLine `xml:"AdrLine"` |
||||
|
} |
||||
|
|
||||
|
func (p *PstlAddr) Valid() error { |
||||
|
var err []string |
||||
|
if !SEPARegexps["Ctry"].MatchString(p.Country) { |
||||
|
err = append(err, "country does not match format") |
||||
|
} |
||||
|
if len(p.AddressLines) > 2 { |
||||
|
err = append(err, "expected at most 2 address lines") |
||||
|
} |
||||
|
for i, line := range p.AddressLines { |
||||
|
if !SEPARegexps["AdrLine"].MatchString(string(line)) { |
||||
|
err = append(err, fmt.Sprintf("address line %d (%q) does not match format", i, line)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if len(err) > 0 { |
||||
|
return fmt.Errorf("address not valid: %v", strings.Join(err, ", ")) |
||||
|
} |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
type CdtrAgt struct { |
||||
|
InstitutionId FinInstnId `xml:"FinInstId"` |
||||
|
} |
||||
|
|
||||
|
func (c *CdtrAgt) Valid() error { |
||||
|
return c.InstituionId.Valid() |
||||
|
} |
||||
|
|
||||
|
type FinInstnId struct { |
||||
|
BIC string `xml:"BIC"` |
||||
|
} |
||||
|
|
||||
|
func (f *FinInstnId) Valid() error { |
||||
|
if !SEPARegexps["BIC"].MatchString(f.BIC) { |
||||
|
return fmt.Errorf("BIC not in expected format") |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
type CdtrSchmeId struct { |
||||
|
Id PartyIdSEPA3 `xml:"Id"` |
||||
|
} |
||||
|
|
||||
|
func (c *CdtrSchmeId) Valid() error { |
||||
|
return c.Id.Valid() |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
package pain |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
type DrctDbtTxInf struct { |
||||
|
Id PaymentId `xml:"PmtId"` |
||||
|
Amount CurrencyWithAmount `xml:"InstdAmt"` |
||||
|
} |
||||
|
|
||||
|
type CurrencyWithAmount struct { |
||||
|
Currency string `xml:"Ccy,attr"` |
||||
|
Value string `xml:",innerxml"` |
||||
|
} |
||||
|
|
||||
|
type PaymentId struct { |
||||
|
InstrumentId string `xml:"InstrId"` |
||||
|
EndToEndId string `xml:"EndToEndId"` |
||||
|
} |
||||
|
|
||||
|
func (p *PaymentId) Valid() error { |
||||
|
var err []string |
||||
|
if !SEPARegexps["InstrId"].MatchString(p.InstrumentId) { |
||||
|
err = append(err, "instrument id does not match format") |
||||
|
} |
||||
|
if !SEPARegexps["EndToEndId"].MatchString(p.EndToEndId) { |
||||
|
err = append(err, "end-to-end id does not match format") |
||||
|
} |
||||
|
|
||||
|
if len(err) > 0 { |
||||
|
return fmt.Errorf("payment id not valid: %v", strings.Join(err, ", ")) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
@ -1,21 +0,0 @@ |
|||||
package sepa |
|
||||
|
|
||||
import ( |
|
||||
"regexp" |
|
||||
) |
|
||||
|
|
||||
var ( |
|
||||
ISODateTime = regexp.MustCompile(`\d{4}(-\d\d){2}T\d\d(:\d\d){2}`) |
|
||||
RestrictedIdentificationSEPA1 = regexp.MustCompile(`([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|'| ]){1,35}`) |
|
||||
RestrictedIdentificationSEPA2 = regexp.MustCompile(`([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){1,35}`) |
|
||||
Max15NumericText = regexp.MustCompile(`[0-9]{1,15}`) |
|
||||
RestrictedDecimalNumber = regexp.MustCompile(`[+-]?\d+\.\d\d`) |
|
||||
Max70Text = regexp.MustCompile(`.{1,70}`) |
|
||||
|
|
||||
ServiceLevelSEPACode = regexp.MustCompile(`SEPA`) |
|
||||
SequenceType1Code = regexp.MustCompile(`FRST|RCUR|FNAL|OOFF`) |
|
||||
PaymentMethod2Code = regexp.MustCompile(`DD`) |
|
||||
LocalInstrumentSEPACode = regexp.MustCompile(`CORE|B2B`) |
|
||||
|
|
||||
IBAN2007Identifier = regexp.MustCompile(`[A-Z]{2,2}[0-9]{2,2}[a-zA-Z0-9]{1,30}`) |
|
||||
) |
|
||||
Loading…
Reference in new issue