package pain import ( "fmt" "strings" "time" ) type DrctDbtTxInf struct { 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 NewDirectDebitTransaction(debtor Dbtr, account DbtrAcct, mandateInfo MndtRltdInf, amount float64, additionalInfo string, counter int) DrctDbtTxInf { e2eid := fmt.Sprintf("%v%04d", time.Now().Format("20060102"), counter) return DrctDbtTxInf{ Id: PaymentId{ EndToEndId: e2eid, }, Amount: CurrencyWithAmount{ Currency: "EUR", Value: fmt.Sprintf("%.2f", amount), }, Transaction: DrctDbtTx{ MandateRelatedInfo: mandateInfo, }, Agent: DbtrAgt{ InstitutionId: FinInstnId{ BIC: "NOTPROVIDED", }, }, Debtor: debtor, Account: account, Info: RmtInf{ Value: additionalInfo, }, } } 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 { Currency string `xml:"Ccy,attr"` 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,omitempty"` 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 } type MndtRltdInf struct { Id string `xml:"MndtId"` SignatureDate string `xml:"DtOfSgntr"` IsAmended bool `xml:"AmdmntInd"` //AmdmntInfDtls is optional //ElctrncSgntr is optional } // NewMandateInfo truncates id to 35 characters func NewMandateInfo(id, signatureDate string) MndtRltdInf { if len(id) > 35 { id = id[0:35] } return MndtRltdInf{ Id: id, SignatureDate: signatureDate, IsAmended: false, } } 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:"FinInstnId"` } func (d *DbtrAgt) Valid() error { return d.InstitutionId.Valid() } type Dbtr struct { Name string `xml:"Nm"` Address PstlAdr `xml:"PstlAdr"` } func NewDebtor(name, line1, line2, country string) Dbtr { return Dbtr{ Name: name, Address: PstlAdr{ Country: country, AddressLines: []AdrLine{ AdrLine(line1), AdrLine(line2), }, }, } } 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:"Id"` } func NewDbtrAcct(iban string) DbtrAcct { return DbtrAcct{ Id: IBAN{ IBAN: 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 }