Package: ottu-py
ottu-pyInstallationpipinstallottu-pyFor Django integration, use the following command.pipinstallottu-py[django]APIsInitializationfromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"))Instead ofAPIKeyAuth, you can also use other authentication methods or your own custom auth classes. For more details, seeauthenticationsection.Apart from thesemerchant_idandauth, you can also pass thecustomer_idandis_sandboxvalues. This is used
while calling the checkout API and other situations.fromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),customer_id="your-customer-id")APIsCheckout (aka Session Create)fromottuimportOttufromottu.authimportAPIKeyAuthfromottu.enumsimportTxnTypeottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),customer_id="your-customer-id")response=ottu.checkout(txn_type=TxnType.PAYMENT_REQUEST,amount="20.23",currency_code="KWD",pg_codes=["mpgs","ottu_pg"],customer_phone="+96550000000",order_no="1234567890",...)print(response)Response{
    "success": true,
    "status_code": 201,
    "endpoint": "/b/checkout/v1/pymt-txn/",
    "response": {
        "amount": "20.230",
        "checkout_url": "https://hotfix.ottu.dev/b/checkout/redirect/start/?session_id=809429a6c912990b195e4e60652436fcae587757",
        "currency_code": "KWD",
        "expiration_time": "00:50:00",
        "session_id": "809429a6c912990b195e4e60652436fcae587757",
        "type": "payment_request",
        ...
    },
    "error": {}
}You can also access theseSessionattributes usingottu.sessionproperty.print(ottu.session.session_id)# 809429a6c912990b195e4e60652436fcae587757print(ottu.session.checkout_url)# https://hotfix.ottu.dev/b/checkout/redirect/start/?session_id=809429a6c912990b195e4e60652436fcae587757It is also possible to use theSession.create(...)to create a new checkout session. That is,ottu.checkout(...)is
an aliase forottu.session.create(...)method.fromottuimportOttufromottu.authimportAPIKeyAuthfromottu.enumsimportTxnTypeottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),customer_id="your-customer-id")response=ottu.session.create(txn_type=TxnType.PAYMENT_REQUEST,amount="20.23",currency_code="KWD",pg_codes=["mpgs","ottu_pg"],customer_phone="+96550000000",order_no="1234567890",...)print(response)Session RetrievefromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),)response=ottu.session.retrieve(session_id="your-session-id")print(response)Session UpdatefromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),)response=ottu.session.update(session_id="your-session-id",amount="1234.23",)print(response)Attachments (Special Case)You can attach the file to session either while creating the session or updating the session usingattachmentargument.attachmentargument accepts a string that represents the path to the file.fromottuimportOttufromottu.authimportAPIKeyAuthfromottu.enumsimportTxnTypeottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),customer_id="your-customer-id")ottu.session.create(txn_type=TxnType.PAYMENT_REQUEST,amount="20.23",currency_code="KWD",pg_codes=["mpgs","ottu_pg"],customer_phone="+96550000000",order_no="1234567890",attachment="path/to/file.pdf",# or "/path/to/file.pdf")# update the session with updated attachmentresponse=ottu.session.update(attachment="path/to/file-1234.pdf",# or "/path/to/file)print(response)Accessing the payment methodsfromottuimportOttufromottu.authimportAPIKeyAuthfromottu.enumsimportTxnTypeottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),customer_id="your-customer-id")ottu.session.create(txn_type=TxnType.PAYMENT_REQUEST,amount="20.23",currency_code="KWD",pg_codes=["mpgs","ottu_pg"],customer_phone="+96550000000",order_no="1234567890",attachment="path/to/file.pdf",# or "/path/to/file.pdf")print(ottu.session.payment_methods)OperationsAll operations are performed on theottu.sessionobject. Also, these methods accept eithersession_idororder_noas an argument. Ifsession_idis not passed, then it will use thesession_idfrom theottu.sessionobject.CancelfromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),)ottu.session.retrieve(session_id="your-session-id")response=ottu.session.cancel()print(response)To specify thesession_idwhile canceling the session, you can pass thesession_idas an argument to
thecancel(...)method.fromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),)response=ottu.session.cancel(session_id="your-session-id")print(response)To specify theorder_nowhile canceling the session, you can pass theorder_noas an argument to thecancel(...)method.fromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),)response=ottu.session.cancel(order_id="your-order-id")print(response)ExpirefromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),)response=ottu.session.expire(session_id="your-session-id")print(response)DeletefromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),)response=ottu.session.delete(session_id="your-session-id")print(response)CapturefromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),)response=ottu.session.capture(session_id="your-session-id",amount="20.23",tracking_key="your-tracking-key")print(response)RefundfromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),)response=ottu.session.refund(session_id="your-session-id",amount="20.23",tracking_key="your-tracking-key")print(response)VoidfromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),)response=ottu.session.void(session_id="your-session-id",tracking_key="your-tracking-key")print(response)CardsList all cards for a customerfromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),customer_id="your-customer-id")response=ottu.cards.list()print(response)Get latest card for a customerfromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),customer_id="your-customer-id")response=ottu.cards.get()print(response)Delete a card for a customer using tokenfromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),customer_id="your-customer-id")response=ottu.cards.delete(token="your-card-token")print(response)Checkout AutoflowCreate a checkout session without specifying the PG codes. The PG codes will be automatically
selected based on the currency code, either from the cache or payment method API.Behind the scenes,ottu-pywill automatically fetch the payment methods from the cache or payment method API and
select the PG codes based on the currency code.fromottuimportOttufromottu.authimportAPIKeyAuthfromottu.enumsimportTxnTypeottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),customer_id="your-customer-id")response=ottu.checkout_autoflow(txn_type=TxnType.PAYMENT_REQUEST,amount="20.23",currency_code="KWD",)print(response)Auto-debit AutoflowCompleting the auto-debit transaction without specifying the PG codes. The PG codes will be automatically
selected based on the currency code from the payment method API. You must pass the value fortokenthat represents the saved card token.Behind the scenes,ottu-pywill automatically fetch the token from the database (only if you use the Django integration) and
select the PG codes based on the currency code by calling the payment method API.fromottuimportOttufromottu.authimportAPIKeyAuthfromottu.enumsimportTxnTypeottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),customer_id="your-customer-id")response=ottu.auto_debit_autoflow(txn_type=TxnType.PAYMENT_REQUEST,amount="20.23",currency_code="KWD",token="your-card-token",agreement={"id":"test-agreement-id",# other agreement attributes},)print(response)Optionally, you can pass thepg_codestoauto_debit_autoflow(...)method. If you pass thepg_codes, then theottu-pywill not call the payment method API to fetch the PG codes.fromottuimportOttufromottu.authimportAPIKeyAuthfromottu.enumsimportTxnTypeottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),customer_id="your-customer-id")response=ottu.auto_debit_autoflow(txn_type=TxnType.PAYMENT_REQUEST,amount="20.23",currency_code="KWD",token="your-card-token",pg_codes=["ottu_pg"],agreement={"id":"test-agreement-id",# other agreement attributes},)print(response)Django IntegrationAddottu.contrib.djangoto yourINSTALLED_APPSsetting.INSTALLED_APPS=[...,"ottu.contrib.django",...,]Set values for the following settings variables.SettingsAuthentication SettingsBasic AuthenticationOTTU_AUTH_USERNAME- Username (example:username)OTTU_AUTH_PASSWORD- Password (example:my-secret-password)API Key AuthenticationOTTU_AUTH_API_KEY- API Key (example:my-secret-api-key)Other SettingsOTTU_MERCHANT_ID- Merchant ID (example:merchant.id.ottu.dev)OTTU_WEBHOOK_KEY- Webhook Key (example:my-secret-webhook-key)OTTU_WEBHOOK_URL- Webhook URL (example:https://your-host.com/path/to/view/)OTTU_IS_SANDBOX- Sandbox environment or not (example:TrueorFalse). Default isFalse.In the case of authentication, it is mandatory to set any set of authentication settings.Checkout# any_module.pyfromottu.contrib.django.core.ottuimportotturesponse=ottu.checkout(txn_type=TxnType.PAYMENT_REQUEST,amount="20.23",currency_code="KWD",pg_codes=["mpgs","ottu_pg"],customer_id="your-customer-id",customer_phone="+96550000000",order_no="1234567890",...)Note: The checkout sessions will be automatically saved (and updated if you configure the webhooks) to the database
if you use theottuinstance.Auto-debit AutoflowSame asAuto-debit Autoflowbut it is optional to pass thetoken. If you don't pass thetoken,ottu-pywill automatically fetch the token from the DB. The token is identified by thecustomer_idandagreement.id.fromottu.contrib.django.core.ottuimportotturesponse=ottu.auto_debit_autoflow(txn_type=TxnType.PAYMENT_REQUEST,amount="20.23",currency_code="KWD",customer_id="your-customer-id",agreement={"id":"test-agreement-id",# other agreement attributes},...)WebhooksTo accept webhooks, you must set bothOTTU_WEBHOOK_KEYandOTTU_WEBHOOK_URLsettings variables. Also, you must
setup the webhook receiver view that comes with the package.# views.pyfromdjango.utils.decoratorsimportmethod_decoratorfromdjango.views.decorators.csrfimportcsrf_exemptfromottu.contrib.django.viewsimportWebhookViewAbstractView@method_decorator(csrf_exempt,name="dispatch")classWebhookReceiverView(WebhookViewAbstractView):pass# urls.pyfromdjango.urlsimportpathfrom.viewsimportWebhookReceiverViewurlpatterns=[path("wh-view/",WebhookReceiverView.as_view(),name="wh-view"),]MiscellaneousAuthenticationTheottu-pysupports a few authentication methods, usually suitable in most of the cases.BasicAuth,APIKeyAuth,KeycloakPasswordAuthandKeycloakClientAuthare the available authentication methods.BasicAuth- Basic authentication, usingusernameandpassword.fromottuimportOttufromottu.authimportBasicAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=BasicAuth(username="your-username",password="your-password"))APIKeyAuth- Authentication using API Key.fromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth(api_key="your-api-key"))KeycloakPasswordAuth- Authentication using Keycloak usingpasswordgrant type.fromottuimportOttufromottu.authimportKeycloakPasswordAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=KeycloakPasswordAuth(username="your-username",password="your-password",client_id="your-client-id",))You may also need to supplyclient_secretif it is required by the Keycloak server.fromottuimportOttufromottu.authimportKeycloakPasswordAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=KeycloakPasswordAuth(username="your-username",password="your-password",client_id="your-client-id",client_secret="your-client-secret"))KeycloakClientAuth- Authentication using Keycloak usingclient_credentialsgrant type.fromottuimportOttufromottu.authimportKeycloakClientAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=KeycloakClientAuth(client_id="your-client-id",client_secret="your-client-secret"))Payment MethodsYou can call theOttu.get_payment_methods(...)method to get the available payment methods for the given merchant.fromottuimportOttufromottu.authimportAPIKeyAuthottu=Ottu(merchant_id="merchant.id.ottu.dev",auth=APIKeyAuth("your-secret-api-key"),)response=ottu.get_payment_methods(plugin="payment_request",currencies=["KWD","USD"],customer_id="your-customer-id",operation="purchase",tokenizable=False,pg_names=[])print(response)Webhook VerificationYou can verify the webhook signature usingverify_signature(...)function.fromottu.utilsimportverify_signaturewebhook_data_received={"amount":"86.000","currency_code":"KWD","customer_first_name":"example-customer",...,# other attributes"signature":"6143b8ad4bd283540721ab000f6de746e722231aaaa90bc38f639081d3ff9f67"}verified=verify_signature(payload=webhook_data_received,signature=webhook_data_received["signature"],# Usually, signature will be sent along with the webhook data.webhook_key=hmac_secret_key_recieved_from_ottu# HMAC Secret Key received from Ottu)API Response StructureAll API calls must have the following structure.{"success":true,"status_code":200,"endpoint":"/path/to/api/endpoint/","response":{},"error":{}}success- Boolean value that represents the success of the API call.status_code- HTTP status code of the API call.endpoint- API endpoint of the last API call.response- Response from the API call.error- Error from the API call.If the call wassuccess, theresponsefield will be present and theerrorfield will be empty, and vice-versa.In most of the cases, theerrorwill be a JSON object with a key ofdetailand value of the error message.{"success":false,"status_code":400,"endpoint":"/path/to/api/endpoint/","response":{},"error":{"detail":"Error message"}}Test# Install the dependenciespipinstall.[test]# Run testspython-mpytestRelease# do a dry-run first -
bump2version --dry-run --verbose [major|minor|patch]

# if everything looks good, run the following command to release
bump2version --verbose [major|minor|patch]

# push the changes to remote
git push origin master --tags