You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

184 lines
4.1 KiB

package pain
import (
"fmt"
"strings"
)
const (
PAIN_XMLNS = "urn:iso:std:iso:20022:tech:xsd:pain.008.002.02"
PAIN_XMLNS_XSI = "http://www.w3.org/2001/XMLSchema-instance"
)
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")
}
for i, pi := range p.PaymentInformation {
if e := pi.Valid(); e != nil {
err = append(err, fmt.Sprintf("payment information at %d has errors: %v", i, e))
}
}
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 {
// Embed PartySEPA2, because we do not need another level of <Id>
PartySEPA2
}
func NewPartyIdSEPA3(id string) PartyIdSEPA3 {
return PartyIdSEPA3{
PartySEPA2{
PrivateId: PersonIdSEPA2{
Other: RestrictedPersonIdSEPA{
Id: id,
SchemeName: RestrictedPersonIdSchemeNameSEPA{
Party: "SEPA",
},
},
},
},
}
}
func (p *PartyIdSEPA3) Valid() error {
return p.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:"Prtry"` // IdentificationSchemeNameSEPA
}
func (r *RestrictedPersonIdSchemeNameSEPA) Valid() error {
if r.Party != "SEPA" {
return fmt.Errorf("party should be 'SEPA', got %v", r.Party)
}
return nil
}