Giter VIP home page Giter VIP logo

solidus_bolt's People

Contributors

danielepalombo avatar kennyadsl avatar naokimi avatar piyushswain avatar waiting-for-dev avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

solidus_bolt's Issues

Bolt Configuration

Bolt is different from a regular payment method in the sense that it also integrates with the user accounts and links them with the user's bolt account.

Since, bolt is a little different from the regular payment method, so we have to store it's configuration separately, so that we can access it outside of payment scenarios.

  1. We should create a new model and table to store this configuration
  2. Bolt should have its own separate tab on the admin panel.
  3. The Bolt page on the admin panel should allow the admin to connect their bolt merchant account after entering some necessary information
  4. We should allow only one bolt merchant account integration per project. If the admin connects a new bolt account then it should override the previous configuration.

Send order tracking details via Bolt API

When Solidus makes changes to an order’s tracking status(updating the tracking_number), Bolt will need to be notified to pass this information to publishers as needed. Solidus will call Bolt’s APIs with updates.

Refund Payment by Webhook

"Refunded" is not a transaction status but a separate transaction that shows the completed
refund. This should not matter, Bolt will still be able to call a Solidus API to notify that the transaction
is refunded, it should just be another status mapping on the Solidus module side.

this is the Refund's payload

{
    "type": "credit",
    "object": "transaction",
    "data": {
        "id": "TAm5vRC1zYUsq",
        "type": "cc_credit",
        "processor": "vantiv",
        "date": 1650658902485,
        "reference": "PQK6-2FBJ-8PND",
        "status": "completed",
        "amount": {
            "amount": 1000,
            "currency": "USD",
            "currency_symbol": "$"
        },
        "order": {
            "cart": {
                "order_reference": "R387090343",
                "display_id": "R387090343",
                "total_amount": {
                    "amount": 1000,
                    "currency": "USD",
                    "currency_symbol": "$"
                },
                "items": [
                    {
                        "reference": "",
                        "name": "Solidus T-Shirt",
                        "total_amount": {
                            "amount": 0,
                            "currency": "USD",
                            "currency_symbol": "$"
                        },
                        "unit_price": {
                            "amount": 1999,
                            "currency": "USD",
                            "currency_symbol": "$"
                        },
                        "tax_amount": {
                            "amount": 0,
                            "currency": "USD",
                            "currency_symbol": "$"
                        },
                        "quantity": 1,
                        "sku": "SOL-00003",
                        "properties": [],
                        "taxable": true,
                        "type": "unknown"
                    }
                ],
                "subtotal_amount": {
                    "amount": 0,
                    "currency": "USD",
                    "currency_symbol": "$"
                },
                "discount_amount": {
                    "amount": 0,
                    "currency": "USD",
                    "currency_symbol": "$"
                }
            }
        },
        "captures": null,
        "refund_transactions": [],
        "source_transaction": {
            "id": "TA9W3jwZtX5eA",
            "reference": "Q4GB-3PK7-YXMR",
            "amount": {
                "amount": 1000,
                "currency": "USD",
                "currency_symbol": "$"
            }
        },
        "risk_signals": {
            "ip_address": "24.5.198.185",
            "time_on_site": "00:00:00",
            "http_headers": {
                "accept": "application/json",
                "accept_encoding": "gzip, deflate, br",
                "accept_language": "en-US,en;q=0.9",
                "connection": "",
                "host": "api-sandbox.bolt.com",
                "referer": "https://merchant-sandbox.bolt.com/",
                "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"
            }
        },
        "capture_type": "manual",
        "refund_type": "single_refund",
        "total_refund_amount": {
            "amount": 1000,
            "currency": "USD",
            "currency_symbol": "$"
        },
        "requested_refund_amount": {
            "amount": 1000,
            "currency": "USD",
            "currency_symbol": "$"
        }
    }
}

Rename BoltCheckout

Considering that the Bolt Checkout should handle the checkout from the beginning retrieving the account information etc.., I think that SolidusBolt::BoltCheckout would be confusing. This is more a BoltPayment or something like that. What do you think to rename it now before it will be too late?

Implement SolidusBolt::Gateway#authorize

Ref https://github.com/nebulab/solidus_bolt/blob/master/app/models/solidus_bolt/gateway.rb#L7-L9

This method will receive the amount, payment_source, and gateway_options and should create Spree::Payment.
The Spree::Payment should have the Transaction.reference, retrieved by Authorize Service stored on the response_code.
It would be nice to store even the credit_card_id on the SolidusBolt::PaymentSource, so by providing a method SolidusBolt::PaymentSource#reusable? which returns true, the payment source will be shown to the user for further payment.

bolt view bug

During the checkout the console has reported an error:

ActionView::Template::Error (Missing partial spree/api/payments/source_views/_bolt with {:locale=>[:en], :formats=>[:json], :variants=>[], :handlers=>[:jbuilder]}. Searched in:
  * "/Users/danielepalombo/Code/solidus_bolt/sandbox/app/views"
  * "/Users/danielepalombo/.asdf/installs/ruby/2.7.6/lib/ruby/gems/2.7.0/gems/solidus_social-1.4.0/app/views"
  * "/Users/danielepalombo/Code/solidus_bolt/app/views"

This should be related with the request:

Image

curl 'http://localhost:3000/api/checkouts/R185775828' \ -X 'PATCH' \ -H 'Accept: */*' \ -H 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \ -H 'Cache-Control: no-cache' \ -H 'Connection: keep-alive' \ -H 'Content-Type: application/json' \ -H 'Origin: http://localhost:3000' \ -H 'Pragma: no-cache' \ -H 'Referer: http://localhost:3000/checkout/payment' \ -H 'Sec-Fetch-Dest: empty' \ -H 'Sec-Fetch-Mode: cors' \ -H 'Sec-Fetch-Site: same-origin' \ -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36' \ -H 'X-Spree-Order-Token: pd9_p9qXw30Td77qeAe6ZQ' \ -H 'sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"' \ -H 'sec-ch-ua-mobile: ?0' \ -H 'sec-ch-ua-platform: "macOS"' \ --data-raw '{"order":{"payments_attributes":[{"payment_method_id":"4","source_attributes":{"card_token":"0db3b56d69830f6d6d3a4af0c4a6fc07064b691fab02d561572bbcabddb920a2","card_last4":"1111","card_bin":"411111","card_expiration":"2022-11","create_bolt_account":true}}]}}' \ --compressed

Capture Payment by Webhook

When a Transaction is in completed state the Payment should be completed in solidus.
In order to prevent capturing the payment again, we have to fill the Spree::Payment#response_code attribute to prevent capturing the payment multiple time.

This is the event payload of the event:

{
    "type": "capture",
    "object": "transaction",
    "data": {
        "id": "TAimWZydQKNUQ",
        "type": "cc_payment",
        "processor": "vantiv",
        "date": 1650649010783,
        "reference": "V2YW-NYNR-2MYM",
        "status": "completed",
        "from_user": {
            "id": "CA7w2RKPi9PKG",
            "first_name": "Daniele",
            "last_name": "Palombo",
            "phones": [
                {
                    "number": "+1 333222334232",
                    "country_code": "1"
                }
            ],
            "emails": [
                {
                    "address": "[email protected]"
                }
            ]
        },
        "from_credit_card": {
            "id": "CAan4YBg23RYj",
            "last4": "1111",
            "bin": "411111",
            "expiration": 1667260800000,
            "network": "Visa",
            "display_network": "Visa",
            "token_type": "bolt",
            "status": "active"
        },
        "amount": {
            "amount": 1200,
            "currency": "USD",
            "currency_symbol": "$"
        },
        "authorization": {
            "auth": "83988858285003417",
            "reason": "none",
            "status": "succeeded"
        },
        "captures": [
            {
                "id": "CAh9W58M3z8Pj",
                "status": "succeeded",
                "amount": {
                    "amount": 1200,
                    "currency": "USD",
                    "currency_symbol": "$"
                }
            }
        ],
        "refund_transactions": [],
        "risk_signals": {
            "ip_address": "79.35.218.113",
            "time_on_site": "00:00:00",
            "http_headers": {
                "accept": "application/json",
                "accept_encoding": "gzip, deflate, br",
                "accept_language": "en-GB,en-US;q=0.9,en;q=0.8",
                "connection": "",
                "host": "api-sandbox.bolt.com",
                "referer": "https://merchant-sandbox.bolt.com/",
                "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"
            }
        },
        "capture_type": "manual"
    }
}

use data.reference to retrieve the payment and advance it to the complete state

Bolt Configuration

Bolt is different from a regular payment method in the sense that it also integrates with the user accounts and links them with the user's bolt account.

Since, bolt is a little different from the regular payment method, so we have to store it's configuration separately, so that we can access it outside of payment scenarios.

  • We should create a new model and table to store this configuration
  • Bolt should have its own separate tab on the admin panel.
  • Allow to the user to set api_key, signing_secret, publishable_key and merchant_id
  1. The Bolt page on the admin panel should allow the admin to connect their bolt merchant account after entering some necessary information, it will be address with #17
  2. We should allow only one bolt merchant account integration per project. If the admin connects a new bolt account then it should override the previous configuration.

Authorize Payment

In order to create the Bolt transaction and store payment on solidus we should get the right information from the js API, and call the Checkout update API providing the right payment and source structure.

Then we have to implement the SolidusGateway::Gateway#authorize method.
This method will receive the amount, payment_source, and gateway_options and should create Spree::Payment.
The PaymentSource should have the credit card info tokenized on the payment source, so that calling Authorize Service it will give back the transaction reference to store on the response_code.
It would be nice to store even the credit_card_id on the SolidusBolt::PaymentSource, so by providing a method SolidusBolt::PaymentSource#reusable? which returns true, the payment source will be shown to the user for further payment.

This is dependent on #13

Merchant Onboarding Flow

The merchant onboarding flow should be built into the extension.

These are the broad steps of the merchant onboarding process.

  1. Bolt Merchant Account Creation
As a first step in the adoption journey, merchants will create a merchant account on Bolt.
This will make use of Bolt-hosted UI and the steps will include information verification for their
Solidus Store information as well as acceptance of Bolt’s terms & policies.

Solidus should provide a link to the admin user that will take them to the merchant account registration page to make it easier for them to adopt bolt.

  1. Payment Processor Addition to Bolt
    Once a Bolt merchant account has been created, the merchant will need to connect to a Bolt supported payment processor in order to start receiving orders.

  2. Bolt API Key, Publishable Key and Signing Secret Generation
    Upon adding the payment processor, Bolt will generate keys required for setting up Bolt on
    Solidus, and redirect the merchant back to Solidus.
    Solidus will need to ensure that the API Key, Publishable Key and Signing Secret are auto-filled on the merchant’s Solidus Store.

Create a Bolt object

In the following months, we should deal with a lot of API calls for Bolt. These will be Merchant, Account, and Transaction APIs. It is worth it to add a "Gateway Object" to handle the requests and responses rather than using the HTTPARTY gem anywhere that would make the request payload creation and authentication hard to use and test.

We have already done it for darkstore with faraday in the past and the process was smooth and we have got a lot of benefits during the development, but we can consider using httparty this time.

Automatically Sign Out Bolt User

solidus_bolt uses the bolt_access_token for a lot of its API calls. This token expires after 15 minutes and when it does, these calls raise a Server Error which block the app.

Since it can very easily happen that a user would be logged in for over 15 minutes, to avoid breaking the user experience we should log out the user whenever the token expires

Add bolt configuration seed

At the moment, BoltConfiguration fetch uses a first_or_create method that has all the configuration field empty.
It would be nice adding the BoltConfiguration to the seed and fill the keys with the BOLT_* variable present on the local environament.

User API endpoint

Solidus will need to build an endpoint for Bolt to call and determine whether there is an existing
merchant storefront account associated with a particular email. This endpoint should accept POST
requests that are signed with Bolt’s Signing Secret and contain the shopper’s email address. When
no account exists, the endpoint should return a 404 error. The endpoint can be whatever Solidus
wants, so long as this is specified to Bolt.

[Epic] [Phase 1] Guest Checkout Integration

See PDF pages 16-20

Goal: A guest shopper can:
● Opt-into creating a Bolt account
● Have their CC saved to their Bolt account
Additionally, order statuses will be synced whether the retailer interacts with Solidus or with Bolt

Steps

  • Initialize Hosted Fields
  • Collect and Tokenize Payments
  • Consent to Bolt Account Creation
  • Localization Support
  • Authorize Payment & Create Bolt Account
  • Order Statuses

Add PaymentSource to User

Reading the payment_methods of the Bolt account by the service created from #76, add the PaymentSource to the user.

This requires a new relation between SolidusBolt::PaymentSource(belongs_to optional: true) and Spree::User(has_many).
As specified on #86, we should store the credit_card_id in order to re-use the source.

Improve README instructions

At the moment the README is not very clear and for new developer would be hard to integrate solidus_bolt in their current application.

We should add some instruction on how and which env variable should be setup and where to go in order to setup the BoltConfiguration.

Void Payment by Webhook

When Transation gets in Cancelled state, we have to void the Solidus Payment.

This is the Event payload we'll receive from Bolt:

{
    "type": "void",
    "object": "transaction",
    "data": {
        "id": "TA5dS8mv4KBHp",
        "type": "cc_payment",
        "processor": "vantiv",
        "date": 1650650380858,
        "reference": "XMM7-KLJZ-K9B3",
        "status": "cancelled",
        "from_user": {
            "id": "CA7w2RKPi9PKG",
            "first_name": "Daniele",
            "last_name": "Palombo",
            "phones": [
                {
                    "number": "+1 333222334232",
                    "country_code": "1"
                }
            ],
            "emails": [
                {
                    "address": "[email protected]"
                }
            ]
        },
        "from_credit_card": {
            "id": "CAan4YBg23RYj",
            "last4": "1111",
            "bin": "411111",
            "expiration": 1667260800000,
            "network": "Visa",
            "display_network": "Visa",
            "token_type": "bolt",
            "status": "active"
        },
        "amount": {
            "amount": 2000,
            "currency": "USD",
            "currency_symbol": "$"
        },
        "authorization": {
            "auth": "83988858395380762",
            "reason": "none",
            "status": "succeeded"
        },
        "captures": null,
        "refund_transactions": [],
        "risk_signals": {
            "ip_address": "79.35.218.113",
            "time_on_site": "00:00:00",
            "http_headers": {
                "accept": "application/json",
                "accept_encoding": "gzip, deflate, br",
                "accept_language": "en-GB,en-US;q=0.9,en;q=0.8",
                "connection": "",
                "host": "api-sandbox.bolt.com",
                "referer": "https://merchant-sandbox.bolt.com/",
                "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"
            }
        },
        "capture_type": "manual"
    }
}

use data.reference to retrieve the payment and advance it to the void state

Create Payment method after merchant onboarding

When the merchant will have completed the Bolt Onboarding process started from solidus, Bolt will redirect the user to a solidus page that should fill all the missing keys in order to let the payment method work.

Following the onboarding UML diagram the Merchant will lend on a solidus page and it should:

  • Make a call to /v1/merchants/<merchant_id>/config endpoint to get API key, Signing Secret & Publishable Key.
  • Store them to the Payment Method created previously when the onboarding process has been started
  • Make a PUT call to /v1/merchants/<merchant_id>/config endpoint to enable the payment
  • Show a success message to the merchant if everything worked as expected

Add oauth/token method to Bolt API Object

We need a new service for oauth/token API that should receive the authorizationCode and Scope and give back access token payload.

{
  "access_token": "DdVJx7rNH1HUp5feAls31Gwe7QLsZTL96_BW3O_OPXI.Jwewe38Pt0uMs2zD-mu89BNu_o2AqXqBoq1C38kQkfHFc",
  "expires_in": 3599,
  "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImU4YTcyNzNkLTQwZDQtNGJlMS1hODI2LWY5MTEzZThiZjBhYiIsInR5cCI6IkpXVCJ9.eyJhdF9oYXNoIjoiNTRBV2tjQ2pfLVBXQjJKWUtMU29FUSIsImF1ZCI6WyJDQl9GdGZHZFVQMmUuUnE0cUIxUWFqWUxuLmMyM2RkYTEwOWZlNDYwYzY5NzlmZjU3OTExYjRhMjU3YjFiNTI1OweZlNjNhY2IyNTE5NjUxMTA0Y2NiMzdkZDUiXSwiYXV0aF90aW1lIjoxNjUxNjA3NzQzLCJjdXN0b21fZmllbGRzIjpbXSwiZW1haWsdfiOiJkYW5pZWxlcGFsb21ibytib2x0QG5lYnVsYWIuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV4cCI6MTY1MTYxMTM0MywiZmlyc3RfbmFtZSI6IkFsZXgiLCJpYXQiOjE2NTE2MDc3NTYsImlzcyI6Imh0dHBzOi8vYXBpLXNhbmRib3guYm9sdC5jb20iLCJqdGkiOiJiYmJkNTBlYy00MjE3LTRmMjgtYjZjNi1jYzUzYTU2YTA5YWEiLCJsYXN0X25hbWUiOiJIb25ub2xkIiwicmF0IjoxNjUxNjA3NzQzLCJzdWIiOiJjZDVkMzA1MS1jODFlLTQ0ZjMtYWViZC05NjkzOTMxM2RmZDQifQ.O5rtD25hi9dOf1b8b7no5_1M6TzPbpqVIx0JWf8JqhZmhZX_51XQ2pgp-tyN5FaQdNllF7qRhwWRgmhWTt2aY9trYLwVvdTdGrVH9GJTdKzAr1BXMczqCFZ6vllvw4IngFd0s63QEX-yb5B-oBBsQAuaT0CitdC36u7rgC4DC7X3Pyn5weQu2hTYU_2_FmK7lcvW-ATZ9D5Y5ApFG7Csn8p-jeRDmOWxsYzFnoxk_2sU69FSVkcRrNc0LtPQU4o6hBLWloEI2E-FHqN6a70fosIWYaZmG4oWtYA839YncNV4nRT_7YfGCRZeaY-fhmydFk80N8K_bIjU8G5reMLVX60yEpV3yt9RFFQeECeIyTJtkMe451UCtXuzDk0BhcVso4B3ldpNzDGS6kuylWs6HCLRk-Aec3JefefbdYOMYps7LUuCrIM9MUNUGxRTRhe095bgeM0B4EHv8mIYHw-bQHmCbPzZeQOi2rsdm4AbHs8EWWsy2ie0HIqlcHVRdPcs9AVs3pU3qROCGMfJKEX8sNkyjlO9bTCO-eiQlWekySceZrFjrlABMKzMfRsljx6Et6dfvFAoKtnn21d4pSxL0Ng1MLvgKpgOtUCO70D51Ua3rC0yS_Wan6NdFz_EgKib4CsBXVQ2Bm0X7LhYblFPKMRK8sr9CJMrMEvn0RK3tMM",
  "scope": "openid bolt.account.manage",
  "token_type": "bearer"
}

Load and initialize embedded component

Following this documentation we have to provide the ability to:

  • Check if the provided email is present on Bolt
  • If the email is present on bolt, show a button beside the email field and ask the Shopper if he wants to sign in with Bolt
  • Start the OTP authentication
  • Forward authorizationCode & Scope to Backend
  • It would be nice having a CSS class selector to specify what are the fields eligible to check the Bolt account.

Update install login/register button

There is a new strategy to load the bolt script and login/register button. We have to integrate it following the instruction provided by:

https://help.bolt.com/developers/guides/checkout-guides/managed-checkout/custom-cart-guides/sso-commerce/#how-to-handle-the-request

After the sign in the user will be redirected on /users/oauth/bolt endpoint and will have the parameters code, state, scope.
In order to let it work with our SSO we have to change the request_phase on omniauth-bolt and the code that redirect the user to that page
It should be enough to replace the authorization_code with just the code work.
In order to verify the authenticity of the account, is required to check also the ID token thought the jwks tokens.

[POC] New merchant onboarding

The new onboarding merchant API will return a payload like this:

{
  "is_enabled": true,
  "config_mode": "<the checkout mode>",
  "merchant_id": "<the merchant ID>",
  "bolt_credentials": {
     "api_key" : "<the API key>",
     "signing_secret" : "<the signing secret>",
     "publishable_key" : "<the publishable key>",
  }
}

We should store this information in the database. In order to do that we should:

  • Add a new model(Singleton?) that should store the above fields
  • Create a new menu item for Bolt.
  • Visiting that page the page will show a button to require to create and link the Bolt account. If the Bolt account is already linked it should show just bolt account information.
  • Create one service that should call the Bolt API, and receiving back the credentials should store them on the Specific model

Add method to handle API responses

All API services are returning the HTTParty response, this means that the caller should deal with HTTP status code and parse the result. This is something that we want to avoid, so it would be nice adding a method to handle the response and give to the caller just the result of the operation.

Enabling automatic syncing of webhook URLs

We'd like to build towards the ideal and most frictionless experience, to achieve that goal it would be nice to allow solidus to set webhook URLs via API.

My proposal is to add a link on the BoltConfiguration page that calls the Bolt API endpoint and create a webhook to the provided route url.

Make the payment_source reusable

During the payment step of the checkout, solidus has to propose the existing payment.
We can achieve that goal using the reusable feature provided by solidus.

Solidus shows to the user all the Spree::PaymentSource that return reusable? == true, so we have to override that method on SolidusBolt::PaymentSource to let it to return true anyway.
Bolt allows to Authorize Card by token or credit_card_id, at the moment the SolidusBolt::PaymentSource#card_id is not set. We should set it after the transaction is authorized taking the id of the card from the response body of the Authorize API using the path [:transaction][:from_credit_cart][:id].
Then we have to change the code to authorize the transaction in order to use the card_id whenever is present.
Add a specific template at app/views/spree/checkout/existing_payment/_bolt.html.erb to show the bolt payment sources.

Start merchant onboarding

The first part of Automated Onboarding for Embedded Checkout is to confirm the merchant details on a Bolt hosted UI.

We have to provide a way for the new merchants to create a Bolt account from the solidus admin interface.

  • Add merchant ID preference to the payment method
  • Add a button on admin payment methods page on the top right corner of the screen to start to create a bolt account
  • Following the UML diagram provided by the bolt team send the first call to the Get onboarding URL, create a new Payment method with the received merchant ID and redirect the user to the Bolt Onboarding Form URL provided by the API

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.