Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

integrate payments via bluefin payconex #93

Open
mbjones opened this issue Apr 19, 2023 · 4 comments
Open

integrate payments via bluefin payconex #93

mbjones opened this issue Apr 19, 2023 · 4 comments

Comments

@mbjones
Copy link
Member

mbjones commented Apr 19, 2023

We have a merchant account through Bluefin Payconex (see payconex developer docs) and need to integrate that in with bookkeeper. See the overview of the process from the dev docs:

image

The basic flow would be that:

  1. User picks products and quantity in browser and clicks Checkout
  2. payconex iframe is generated to collect user credit card data
  3. payconex iframe sends that data to payconex, which returns an eToken
  4. js code in browser app sends the eToken to bookkeeper
  5. bookkeeper sends a QSAPI request to payconex with the etoken
  6. payconex responds to bookkeeper with approve/decline
  7. bookkeeper updates the database with the payment status and returns status to browser client
@mbjones
Copy link
Member Author

mbjones commented Apr 20, 2023

Initial thoughts on implementation sequence using Payconex's HostedForm utility that embeds an iFrame for encrypted credit card details.

%%{init: {
  "theme": "forest",
  "sequence": { "showSequenceNumbers": true }
}}%%
sequenceDiagram
    participant Bookkeeper
    participant Purser
    participant Payconex

    note left of Purser: Set up products form
    Purser->>+Bookkeeper: GET /bookkeeper/v1/products
    Bookkeeper-->>-Purser: productList
    Purser->>Purser: showProductForm()

    rect rgb(200, 223, 255)
    note right of Purser: Create order and set up payment form
    Purser->>Purser: checkout()
    Purser->>+Bookkeeper: POST /bookkeeper/v1/order
    Bookkeeper->>Bookkeeper: storeOrder()
    Bookkeeper-->>-Purser: orderId

    Purser->>+Payconex: update_iframe(orderid, productlist, amount, user details)
    Payconex-->>-Purser: iFrame
    end

    rect rgb(170, 223, 255)
    note right of Bookkeeper: Process payment and notify Bookkeeper
    Purser->>+Payconex: pay(orderid, productlist, amount)
    Payconex->>Purser: receipt
    
    Payconex->>-Bookkeeper: POST /bookkeeper/v1/order/{orderid}/pay OrderStatus
    activate Bookkeeper
    Bookkeeper->>Bookkeeper: updateOrder()
    deactivate Bookkeeper
    end

    Purser->>+Bookkeeper: GET /bookkeeper/v1/order/{orderid}
    Bookkeeper-->>-Purser: order
Loading

@mbjones
Copy link
Member Author

mbjones commented Apr 22, 2023

A state diagram for order transitions. States from the SQL table definition comments.

stateDiagram-v2
    direction LR
    [*] --> Created
    Created --> Canceled
    Created --> Paid
    Paid --> Returned
    Paid --> Fulfilled
    Canceled --> [*]
    Fulfilled --> [*]
    Returned --> [*]
Loading

Alternative with states from the Order.java representation class. I don't see any way to make these make total sense.

active|created|paid|past_due|refunded|trialing|unpaid

stateDiagram-v2
    direction LR
    [*] --> Created
    Created --> PastDue
    Created --> Unpaid
    Created --> Paid
    Created --> Trialing
    Paid --> Refunded
    Paid --> Active
    Trialing --> Active
    Active --> [*]
    Refunded --> [*]
Loading

Alternative as a hybrid of these to be evaluated:

active|created|paid|expired|refunded|trialing

stateDiagram-v2
    direction LR
    [*] --> Created
    Created --> Paid
    Created --> Trialing
    Trialing --> Paid
    Paid --> Paid
    Trialing --> Expired
    Created --> Canceled
    Paid --> Refunded
    Paid --> Expired
    Canceled --> [*]
    Expired --> [*]
    Refunded --> [*]
Loading

@mbjones
Copy link
Member Author

mbjones commented May 16, 2023

Initial design of PaymentsResource implemented in SHA dcdd9e8 and SHA 523034a52237. The new REST service lives at the endpoint /bookkeeper/v1/payments, and the only implemented method is currently a POST which enables creation of a new payment transaction. This transaction takes the form of a postback transaction log from the transaction processor (in this case Bluefin Payconex) which has the following format:

Payment Transaction JSON format
{
    "account_id": "351818369912",
    "timestamp": 1374346390,
    "count": 1,
    "hash": "c3a0b8cddacab5c761fd61d9176013e9287384938b0c2362b6b4bc090a257c6a",
    "responses": [
        {
            "transaction_id": "000000005351",
            "tender_type": "CARD",
            "transaction_timestamp": "2022-04-27 18:40:16",
            "card_brand": "VISA",
            "transaction_type": "SALE",
            "last4": "1111",
            "card_expiration": "0326",
            "authorization_code": "OK2826",
            "authorization_message": "APPROVED",
            "request_amount": "575.04",
            "transaction_amount": "575.04",
            "fid": "31721",
            "ip_address": "24.237.6.75",
            "first_name": "Matthew",
            "last_name": "Jones",
            "keyed": "1",
            "swiped": "0",
            "entry_mode": "keyed",
            "transaction_approved": "1",
            "avs_response": "Y",
            "reason_code": "000",
            "trace_number": "000000006901",
            "network": "VISA",
            "currency": "USD",
            "error": "",
            "error_code": "0",
            "error_message": "",
            "error_msg": "",
            "orderid": "33924",
            "products": "DataONEPlus(1);",
            "first_last_name": "",
            "g-recaptcha-response": "03AGdBq26JR5c-GBYAZKrejJwJX4FFaHejaOmEFnJVrpT33WL3N8hq9VBVbCzaIwzSGUYrcAZiJ9LT_U_-GGxgo5kQS8AJS4YFhcsEdbpePloX1VdA1HGDnNaqEsdqD12doWwvdrVFy15l6u8tPBzjm07eSGoh4qGBBAmo5nxx0CdATHknnX55dCdaNzZnXI6bQyzvTFAAxoJ1otlyvQb9gWgz4PrrPVju7FwCPPOtNSL2rG9skhspfi5SaUo2-paM-LIYlpUb0hJT8JVHfLbYBA8GFNhZs1d5IlKeGLbsen2UQQAKd3cYt_H5Q5hFtTUjlx_tPIUkArc2xad-edd8IFEbYaQIpATXf8suVSaKBt5QFqdeI3VBGZaX8CUMVBNC3io3NaVX0kx-bcluAfHe6k9PE4u9YztUtlXMHfw-B92Q5xc6_4OWZ_5yRSG93Lja4oORxS7cNnrn",
            "hiddenRecaptcha": "",
            "hosted_payment_signal": "1",
            "callback_method": "GET",
            "expressCheckOutPayload": "",
            "description": "Amount"
        }
    ]
}

Still much to do, including:

  • Create a Payment POJO for payment transactions
  • Create a PaymentsResource class for the payments callback REST API
  • Finish implementing the Payment JSON deserializer with rest of the field (currently, I only picked out the critical ones)
  • Validate the payment has using the secret API key
  • Update the database
    • Determine if the payment transaction itself deserves a new db table outside of orders to trans payment details: YES
    • Create a new payments table to hold payment transaction records
    • Update the payment transactions table when a payment is submitted
    • Update the corresponding order table to indicate the order is paid
  • call the Order.pay() when a transaction is approved to update quotas and usages and migrate state transitions
  • Handle refunds
  • Handle cancellations
  • Review all business logic
  • Add tests
  • Add documentation to new classes

@mbjones
Copy link
Member Author

mbjones commented May 16, 2023

Revised diagram on implementation sequence using Payconex's HostedForm utility that embeds an iFrame for encrypted credit card details, with the new approach of using postback transaction receipts to finalize the transaction on the server side:

%%{init: {
  "theme": "forest",
  "sequence": { "showSequenceNumbers": true }
}}%%
sequenceDiagram
    participant Bookkeeper
    participant Purser
    participant Payconex

    note left of Purser: Set up products form
    Purser->>+Bookkeeper: GET /bookkeeper/v1/products
    Bookkeeper-->>-Purser: productList
    Purser->>Purser: showProductForm()

    rect rgb(200, 223, 255)
    note right of Purser: Create order and set up payment form
    Purser->>Purser: checkout()
    Purser->>+Bookkeeper: POST /bookkeeper/v1/order
    Bookkeeper->>Bookkeeper: storeOrder()
    Bookkeeper-->>-Purser: orderId

    Purser->>+Payconex: update_iframe(orderid, productlist, amount, user details)
    Payconex-->>-Purser: iFrame
    end

    rect rgb(170, 223, 255)
    note right of Bookkeeper: Process payment and notify Bookkeeper
    Purser->>+Payconex: pay(orderid, productlist, amount)
    Payconex->>Purser: receipt
    
    Payconex->>-Bookkeeper: POST /bookkeeper/v1/payments Payment
    activate Bookkeeper
    Bookkeeper->>Bookkeeper: validateTransaction()
    Bookkeeper->>Bookkeeper: processPayment()
    Bookkeeper->>Payconex: HTTP 200 or Error
    deactivate Bookkeeper
    end

    Purser->>+Bookkeeper: GET /bookkeeper/v1/order/{orderid}
    Bookkeeper-->>-Purser: order
    note right of Purser: Update UI based<br> on order status
Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant