2 changed files with 166 additions and 0 deletions
@ -0,0 +1,147 @@ |
|||||
|
package sepa |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"regexp" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
var ( |
||||
|
SEPARegexps = map[string]*regexp.Regexp{ |
||||
|
"MsgId": RestrictedIdentificationSEPA1, |
||||
|
"CreDtTm": ISODateTime, |
||||
|
"NbOfTxs": Max15NumericText, |
||||
|
"CtrlSum": RestrictedDecimalNumber, |
||||
|
"Nm": Max70Text, |
||||
|
"PmtInfId": RestrictedIdentificationSEPA1, |
||||
|
"PmtMtd": PaymentMethod2Code, |
||||
|
"PmtMetaSvcLvl": ServiceLevelSEPACode, |
||||
|
"PmtMetaLclInstrm": LocalInstrumentSEPACode, |
||||
|
"SeqTp": SequenceType1Code, |
||||
|
} |
||||
|
) |
||||
|
|
||||
|
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") |
||||
|
} |
||||
|
// TODO
|
||||
|
/* |
||||
|
if g.InitiatingParty == nil { |
||||
|
err = append(err, "no initiating party found") |
||||
|
} else { |
||||
|
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"` |
||||
|
} |
||||
|
|
||||
|
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 info id does not match expected format") |
||||
|
} |
||||
|
if e := pmt.PaymentMeta.Valid(); e != nil { |
||||
|
err = append(err, fmt.Sprintf("payment meta 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 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"` |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
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`) |
||||
|
) |
||||
Loading…
Reference in new issue