package pain import ( "fmt" "strings" ) const ( PAIN_XMLNS_XSI = "http://www.w3.org/2001/XMLSchema-instance" PAIN_XMLNS = "urn:iso:std:iso:20022:tech:xsd:pain.008.001.02" ) type Document struct { XmlnsXsi string `xml:"xmlns:xsi,attr"` Namespace string `xml:"xmlns,attr"` Contents *PainXML `xml:"CstmrDrctDbtInitn"` } func (d *Document) Valid() error { var err []string if d.XmlnsXsi != PAIN_XMLNS_XSI { err = append(err, "xmlns:xsi does not match PAIN_XMLNS_XSI") } if d.Namespace != PAIN_XMLNS { err = append(err, "xmlns does not match PAIN_XMLNS") } if e := d.Contents.Valid(); err != nil { err = append(err, fmt.Sprintf("%v", e)) } if len(err) > 0 { return fmt.Errorf("document not valid: %v", strings.Join(err, ", ")) } return nil } type PainXML struct { GroupHeader *GrpHdr `xml:"GrpHdr"` PaymentInformation []PmtInf `xml:"PmtInf"` } 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(err) > 0 { return fmt.Errorf("pain XML not valid: %v", strings.Join(err, ", ")) } return nil } type CdtrAcct struct { Id IBAN `xml:"Id"` } func (c *CdtrAcct) Valid() error { if e := c.Id.Valid(); e != nil { return fmt.Errorf("creditor account id not valid: %v", e) } 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") } 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 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 NewPartyIdSEPA3(id string) PartyIdSEPA3 { return PartyIdSEPA3{ Id: PartySEPA2{ PrivateId: PersonIdSEPA2{ Other: RestrictedPersonIdSEPA{ Id: id, SchemeName: RestrictedPersonIdSchemeNameSEPA{ Party: "SEPA", }, }, }, }, } } 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 }