|
|
|
|
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
|
|
|
|
|
}
|