Description
Revision of how lastWeekDay is expressed in the Scheduled Payments payloads.
Area Affected
The payloads for the Scheduled Payments end points.
Change Proposed
This issue is raised in response to a feedback comment in the main standards repository, ConsumerDataStandardsAustralia/standards#73 (comment)
This feedback is quoted below:
Further, BankingScheduledPaymentRecurrenceLastWeekday
and BankingScheduledPaymentInterval
appear to have similar purposes but use different data types (Duration
vs. Integer
) when specifying a day of week.
This variation is deliberate. The interval in BankingScheduledPaymentInterval can potentially be many days so the ability to represent this interval in terms on years, months or days is more appropriate. As lastWeekDay is constrained to days in the week an integer was seen as more applicable than using a duration field and limiting the value to exclude the majority of the duration syntax.
If lastWeekDay
is intended to be a day of week then the least confusing method would be to declare and reuse a DayOfWeek
enumeration which strongly orders the days of week in line with ISO-8601.
Regarding dayInInterval
in BankingScheduledPaymentInterval
, is this intended to be an arbitrary number of days for the interval period? Ie. If BankingScheduledPaymentInterval
represents a monthly payment but the payment is to be conducted on the 5th day of every month interval
=P1M
and dayInInterval
=5
?
ISO-8601 allows for bounded durations specified using a /
separator. Additionally the standard allows for recurring time intervals to be specified using a prefix of R/#
. Are these variations intended to be supported?
Finally, the BankingScheduledPayment* namespaces appear to have shared attribues in sub objects (eg. paymentsRemaining
) rather than following an attribute structure similar to Product info's use of additionalValue
and additionalInfo
.
The preference in this case is to be strongly typed rather than use the more generic model used in product reference data. These objects may initially be similar but, as they represent different conceptual objects, they may diverge over time. As such dedicated structures for each interval type have been defined.
As per above ISO-8601 allows for bounded timespans and repetition. Since this is facilitated in ISO-8601 BankingScheduledPaymentRecurrenceOnceOff
, BankingScheduledPaymentRecurrenceLastWeekday
and BankingScheduledPaymentRecurrenceIntervalSchedule
all appear to be able to be represented in a single unified object.
Nonetheless, specifically on BankingScheduledPaymentRecurrenceOnceOff
there doesn't appear to be a facilitation for nonBusinessDayTreatment
. On BankingScheduledPaymentRecurrenceIntervalSchedule
the finalPaymentDate
could be derived from a bounded ISO-8601 Duration. On BankingScheduledPaymentRecurrenceLastWeekday
there doesn't appear to be a facilitation for nonBusinessDayTreatment
(ie. when the weekday is set to Monday and it's a public holiday).
The uType convention provides reference to a JSON field. In most cases this is an object but it is not necessarily always the case. In this case it is referencing only a single field but was included because it is expected that new types will be added in the future as new NPP overlay services become available.
My feedback has been misunderstood. There is a high probability that there will be additional transaction details that are divergent. In the current proposal extendedData
and, on further review BankingTransaction
effectively contains all possible payment attributes which inevitably will lead to a sparsely populated and poorly structured object. This means that a developer will need to perform picklist matching to determine the acquisition channel rather than be able to determine that acquisition channel via a strongly typed subobject.
On BankingTransaction
, overloading of the base object is occurring rather than using the uType
convention followed elsewhere. Specifically of note is that BPAY payments (a trademarked and commercially attached payment provider) have been granted exclusivity over generically named attributes notably billerCode
, billerName
and crn
. This precludes an alternate payments provider using these global attributes and arguably provides a commercial advantage to the incumbent. Additionally reference
described as The reference for the transaction provided by the originating institution. Empty string if no data provided
appears to be overloaded as it may be used by various origination sources.
BankingTransactionDetail
does not appear to sufficiently allow for attributes of even existing payment types such as:
- BPay: BPAY reference id
- Traditional payments: BSB, Account Number etc
- International payments: Routing codes, payment instructions, intermediaries etc.
- NPP: Currently only
pain.a09.001.01
exists but it is likely this sub-object itself would need an NPPuType
- Internal Transfers: Source/Destination account (probably using the UUID of the Accounts in question)
- ISO20022 Payments: Current consultation is being conducted by the RBA for mandatory adoption
As can be seen through the collapsing of these structures there is an increasing amount of fuzziness for a recipient developer attempting to clearly identify and present transaction information. Such different views are a regular use case within existing internet banking platforms. That is to say that BPAY transactions usually have a different presentation view than direct transfers which have a different presentation view to those which utilise card providers such as VISA or Mastercard.
Pseudo coding an alternate JSON structure for BankingTransaction:
"BankingTransaction" : {
"type" : "object",
"required" : [ "accountId", "amount", "description", "isDetailAvailable", "reference", "status", "type" ],
"properties" : {
"accountId" : {
"type" : "string",
"description" : "ID of the account for which transactions are provided",
"x-cds-type" : "ASCIIString"
},
"transactionId" : {
"type" : "string",
"description" : "A unique ID of the transaction adhering to the standards for ID permanence. This is mandatory (through hashing if necessary) unless there are specific and justifiable technical reasons why a transaction cannot be uniquely identified for a particular account type",
"x-cds-type" : "ASCIIString"
},
"isDetailAvailable" : {
"type" : "boolean",
"description" : "True if extended information is available using the transaction detail end point. False if extended data is not available",
"x-cds-type" : "Boolean"
},
"holderType" : {
"type" : "string",
"description" : "The type of the transaction within the Holders sytems",
"enum" : [ "FEE", "INTEREST_CHARGED", "INTEREST_PAID", "TRANSFER_OUTGOING", "TRANSFER_INCOMING", "PAYMENT", "DIRECT_DEBIT", "OTHER" ]
},
"status" : {
"type" : "string",
"description" : "Status of the transaction whether pending or posted. Note that there is currently no provision in the standards to guarantee the ability to correlate a pending transaction with an associated posted transaction",
"enum" : [ "PENDING", "POSTED" ]
},
"description" : {
"type" : "string",
"description" : "The transaction description as applied by the financial institution"
},
"postingDateTime" : {
"type" : "string",
"description" : "The time the transaction was posted. This field is Mandatory if the transaction has status POSTED. This is the time that appears on a standard statement",
"x-cds-type" : "DateTimeString"
},
"valueDateTime" : {
"type" : "string",
"description" : "Date and time at which assets become available to the account owner in case of a credit entry, or cease to be available to the account owner in case of a debit transaction entry",
"x-cds-type" : "DateTimeString"
},
"executionDateTime" : {
"type" : "string",
"description" : "The time the transaction was executed by the originating customer, if available",
"x-cds-type" : "DateTimeString"
},
"amount" : {
"type" : "string",
"description" : "The value of the transaction. Negative values mean money was outgoing from the account",
"x-cds-type" : "AmountString"
},
"currency" : {
"type" : "string",
"description" : "The currency for the transaction amount. AUD assumed if not present",
"x-cds-type" : "CurrencyString"
},
"reference" : {
"type" : "string",
"description" : "The reference for the transaction provided by the originating institution. Empty string if no data provided"
},
"transactionUType" : {
"type" : "string",
"description" : "Type of object included that describes the transaction, reused for TransactionDetail",
"enum" : [ "BPAY", "PAYID", "WIRE", "DIRECT" ]
},
"BPAY" : {
"$ref" : "#/definitions/BankingTransactionBPAY"
},
"PAYID" : {
"$ref" : "#/definitions/BankingTransactionPayID"
},
"WIRE" : {
"$ref" : "#/definitions/BankingTransactionWire"
}
"DIRECT" : {
"$ref" : "#/definitions/BankingTransactionDirect"
}
},
},
Pseudo coding a JSON structure for BankingTransactionDetail:
"BankingTransactionDetail" : {
"allOf" : [ {
"$ref" : "#/definitions/BankingTransaction"
}, {
"type" : "object",
"required" : [ "transactionUType" ],
"properties" : {
"BPAY" : {
"$ref" : "#/definitions/BankingTransactionDetailBPAY"
},
"PAYID" : {
"$ref" : "#/definitions/BankingTransactionDetailPayID"
},
"WIRE" : {
"$ref" : "#/definitions/BankingTransactionDetailWire"
}
"DIRECT" : {
"$ref" : "#/definitions/BankingTransactionDetailDirect"
}
},
"x-conditional" : [ "WIRE", "BPAY", "WIRE", "DIRECT" ]
} ]
}
This structure allows BankingTransactionDetail*
to extend the non detail version of the object while rewriting it within the detailed definition. Additionally this structure allows for non-standard payment types to be adopted by banks (for instance an Apple Pay integration) without polluting the namespace. Finally this structure makes transitioning to polymorphism available in OAI3 using the uType
as the discriminator.
Thanks for the detailed review. Most of this is stylistic and will be incorporated.
Repeating one of the defined principles: Principle 4: APIs provide a good developer experience
Stylistic from the Standards perspective is object pollution for developers using available industry tools. I would suggest that perhaps Standards should consider running swagger-codegen
on their specification as a matter of course.
The response code table will be updated as suggested.
Regarding @anzbankau's comment and your response regarding the return of 422 Unprocessable Entity
for invalid pagination inputs. This is divergent from the UK OpenBanking specification which states that 400 Bad Request
is to be used in the situation defined as Request has malformed, missing or non-compliant JSON body, URL parameters or header fields.
. In fact error code 422, defined in HTTP 1.1+, is entirely missing from the UK Standards. Instead the UK Standards prefer a HTTP 1.0 400 Bad Request
and this convention is utilised within Engineering artefacts (following clarification at the time with Standards). Is this position now changing?
Thanks.