ConsumerDataStandardsAustralia / standards-maintenance

This repository houses the interactions, consultations and work management to support the maintenance of baselined components of the Consumer Data Right API Standards and Information Security profile.
41 stars 9 forks source link

Using Duration standard for lastWeekDay in Scheduled Payments #5

Closed CDR-API-Stream closed 4 years ago

CDR-API-Stream commented 5 years ago

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, https://github.com/ConsumerDataStandardsAustralia/standards/issues/73#issuecomment-511268647

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 BankingScheduledPaymentRecurrenceLastWeekdaythere 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:

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.

CDR-API-Stream commented 5 years ago

Apologies for the delay in responding on this issue. It is quite complex as it really represents a bundling of multiple suggested changes and clarifications (which is understandable considering the linkages between the issues).

Recommendations for the proposed changes will be outlined below. Note that, as scheduled payments is not planned for February and no holders have indicated early implementation these changes will be addressed by updating version 1 of the end points rather than creating a version 2 and managing obsolescence. If this position creates an implementation issue then feedback to this end is requested.

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.

This change will be recommended by the DSB.

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 ?

dayInInterval also utilises ISO-8601 so, in the example above the value of dayInInterval would be P5D. This approach was chosen to accommodate periods longer than a month. For example, specifying the 5th day of the second month each quarter would be represented as: interval = P3M dayInInterval = P1M5D

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?

Support for recurring intervals is not intended. It is recommended that this be clarified in field description.

This approach was selected for two reasons:

  1. The recurring structure in ISO-8601 by itself will not support all of the variations identified by data holders
  2. The recurring structure in ISO-8601 will support combinations that are not supported by data holders which would impact voluntary write/update scenarios

Nonetheless, specifically on BankingScheduledPaymentRecurrenceOnceOff there doesn't appear to be a facilitation for nonBusinessDayTreatment.

BankingScheduledPaymentRecurrenceOnceOff is intended to represent a single known payment date so it is assumed that business day treatment has been included in the setting of the specified date.

On BankingScheduledPaymentRecurrenceLastWeekdaythere doesn't appear to be a facilitation for nonBusinessDayTreatment (ie. when the weekday is set to Monday and it's a public holiday).

The addition of nonBusinessDayTreatment to the last week day structure will be recommended.

In regard to the remainder of the proposal that the transaction object being converted from a mixin style structure to a more structured model, this was considered but the variation of data held across various data holders indicated that the exceptions to a structured model would make this prohibitive. For the first iteration of the standards a mixin approach was therefor taken.

The DSB does not recommmend changing this approach at the current time. It is suggested that this is revisited once a transaction data has been made available for all account types to see if a structured model can be introduced.