dana-python 2.2.1__tar.gz → 3.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dana_python-2.2.1/dana_python.egg-info → dana_python-3.0.0}/PKG-INFO +7 -10
- {dana_python-2.2.1 → dana_python-3.0.0}/README.md +1 -1
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/snap_configuration.py +3 -3
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/webhook.py +38 -16
- dana_python-3.0.0/dana/widget/v1/util.py +338 -0
- {dana_python-2.2.1 → dana_python-3.0.0/dana_python.egg-info}/PKG-INFO +7 -10
- {dana_python-2.2.1 → dana_python-3.0.0}/dana_python.egg-info/SOURCES.txt +3 -0
- dana_python-3.0.0/dana_python.egg-info/requires.txt +7 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/pyproject.toml +6 -9
- dana_python-3.0.0/tests/test_dependency_floors.py +25 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_payment_gateway_api.py +14 -1
- {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_payment_gateway_with_automation.py +13 -71
- dana_python-3.0.0/tests/test_snap_configuration.py +15 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_webhook.py +66 -0
- dana_python-3.0.0/tests/test_webhook_key_selection.py +35 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_widget_api.py +9 -3
- {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_widget_with_automation.py +6 -5
- dana_python-2.2.1/dana/widget/v1/util.py +0 -289
- dana_python-2.2.1/dana_python.egg-info/requires.txt +0 -10
- {dana_python-2.2.1 → dana_python-3.0.0}/LICENSE +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/api_client.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/api_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/base/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/base/configuration.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/base/model.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/base/types.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/api/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/api/disbursement_api.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/custom_validation.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/enum.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/bank_account_inquiry_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/bank_account_inquiry_request_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/bank_account_inquiry_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/bank_account_inquiry_response_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/dana_account_inquiry_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/dana_account_inquiry_request_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/dana_account_inquiry_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/money.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_bank_inquiry_status_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_bank_inquiry_status_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_bank_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_bank_request_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_bank_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_dana_inquiry_status_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_dana_inquiry_status_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_dana_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_dana_request_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_dana_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/exceptions.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/api/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/api/merchant_management_api.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/enum.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/address_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/asset_card_list_item.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/business_docs.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_division_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_division_request_ext_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_division_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_division_response_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_division_response_response_body.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_division_response_response_head.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_shop_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_shop_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_shop_response_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_shop_response_response_body.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_shop_response_response_head.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/division_resource_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/member_asset_result_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_account_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_certificate_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_contact_address.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_contact_email.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_contact_mobile_no.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_corporate_certificate.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_information.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_resource_information.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/mobile_no_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/pic_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_asset_card_list_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_asset_card_list_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_asset_card_list_response_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_asset_card_list_response_response_body.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_asset_card_list_response_response_head.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_division_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_division_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_division_response_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_division_response_response_body.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_division_response_response_head.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_info_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_info_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_info_response_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_info_response_response_body.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_info_response_response_head.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_resource_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_resource_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_resource_response_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_resource_response_response_body.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_resource_response_response_head.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_shop_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_shop_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_shop_response_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_shop_response_response_body.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_shop_response_response_head.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/result_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/shop_resource_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_division_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_division_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_division_response_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_division_response_response_body.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_division_response_response_head.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_shop_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_shop_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_shop_response_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_shop_response_response_head.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/user_name.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/api/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/api/payment_gateway_api.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/custom_validation.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/enum.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/actor_context.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/amount_detail.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/audit_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/buyer.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/cancel_order_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/cancel_order_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/consult_pay_payment_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/consult_pay_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/consult_pay_request_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/consult_pay_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/create_order_by_api_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/create_order_by_api_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/create_order_by_redirect_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/create_order_by_redirect_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/create_order_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/create_order_response_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/env_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/goods.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/money.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/order_api_object.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/order_redirect_object.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/pay_option_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/pay_option_detail.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/pay_option_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/payment_view.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/promo_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/query_payment_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/query_payment_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/query_payment_response_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/refund_option_bill.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/refund_order_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/refund_order_request_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/refund_order_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/seller.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/shipping_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/status_detail.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/time_detail.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/url_param.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/virtual_account_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/rest.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/date_validation.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/models.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/open_api_configuration.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/open_api_header.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/script.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/snap_header.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/url.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/finish_notify_payment_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/finish_notify_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/finish_notify_request_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/finish_notify_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/money.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/pay_option_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/shop_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/api/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/api/widget_api.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/custom_validation.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/enum.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/__init__.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/account_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/account_unbinding_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/account_unbinding_request_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/account_unbinding_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/amount_detail.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_ott_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_ott_request_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_ott_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_ott_response_user_resources_inner.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_token_authorization_code_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_token_refresh_token_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_token_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_token_response_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_token_response_additional_info_user_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/balance_inquiry_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/balance_inquiry_request_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/balance_inquiry_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/buyer.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/cancel_order_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/cancel_order_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/env_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/goods.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/international_order_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/international_order_info_exchange_rate.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/money.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/oauth2_url_data.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/oauth2_url_data_seamless_data.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/order.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/pay_option_detail.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/pay_option_detail_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/pay_option_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/payment_promo_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/payment_view.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_payment_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_payment_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_payment_response_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_user_profile_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_user_profile_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_user_profile_response_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_user_profile_response_response_body.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_user_profile_response_response_head.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/refund_order_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/refund_order_request_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/refund_order_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/refund_promo_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/result_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/seller.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/service_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/shipping_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/status_detail.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/time_detail.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/url_param.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/user_resource_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/virtual_account_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/widget_payment_request.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/widget_payment_request_additional_info.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/widget_payment_response.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana_python.egg-info/dependency_links.txt +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/dana_python.egg-info/top_level.txt +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/setup.cfg +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_disbursement_api.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_disbursement_custom_validation_enrich.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_merchant_management_api.py +0 -0
- {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_snap_header.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dana-python
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: API Client (SDK) for DANA APIs based on https://dashboard.dana.id/api-docs
|
|
5
5
|
Author-email: DANA Package Manager <package-manager@dana.id>
|
|
6
6
|
Maintainer-email: DANA Package Manager <package-manager@dana.id>
|
|
@@ -8,19 +8,16 @@ License-Expression: Apache-2.0
|
|
|
8
8
|
Project-URL: homepage, https://dashboard.dana.id/api-docs
|
|
9
9
|
Project-URL: repository, https://github.com/dana-id/dana-python
|
|
10
10
|
Keywords: DANA,DANA ID Docs,DANA SDK,DANA Python,DANA API Client,DANA Python API Client,DANA Python SDK
|
|
11
|
-
Requires-Python:
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
-
Requires-Dist: annotated-types
|
|
15
|
-
Requires-Dist:
|
|
16
|
-
Requires-Dist: cryptography<46.0.0,>=44.0.2
|
|
17
|
-
Requires-Dist: pycparser==2.23
|
|
14
|
+
Requires-Dist: annotated-types>=0.7.0
|
|
15
|
+
Requires-Dist: cryptography>=50.0.0
|
|
18
16
|
Requires-Dist: pydantic<3.0.0,>=2.10.6
|
|
19
17
|
Requires-Dist: pydantic-core<3.0.0,>=2.27.2
|
|
20
|
-
Requires-Dist: python-dateutil
|
|
21
|
-
Requires-Dist: six==1.17.0
|
|
18
|
+
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
22
19
|
Requires-Dist: typing-extensions<5.0.0,>=4.12.2
|
|
23
|
-
Requires-Dist: urllib3<3.0.0,>=2.
|
|
20
|
+
Requires-Dist: urllib3<3.0.0,>=2.8.0
|
|
24
21
|
Dynamic: license-file
|
|
25
22
|
|
|
26
23
|
# dana-python
|
|
@@ -62,7 +59,7 @@ Here is the link: https://github.com/dana-id/uat-script.
|
|
|
62
59
|
|
|
63
60
|
## Requirements.
|
|
64
61
|
|
|
65
|
-
Python 3.9.1
|
|
62
|
+
Python 3.10+ (patched releases: 3.11.17, 3.12.15, 3.13.17, 3.14.9, 3.15.1 or later)
|
|
66
63
|
|
|
67
64
|
## Installation & Usage
|
|
68
65
|
### pip install
|
|
@@ -231,9 +231,9 @@ class SnapConfiguration(BaseConfiguration):
|
|
|
231
231
|
) -> None:
|
|
232
232
|
"""Constructor
|
|
233
233
|
"""
|
|
234
|
-
self.host = "
|
|
234
|
+
self.host = "https://api.sandbox.dana.id" if (api_key["DANA_ENV"] == Env.SANDBOX or api_key["ENV"] == Env.SANDBOX) else \
|
|
235
235
|
"https://api.saas.dana.id" if (api_key["DANA_ENV"] == Env.PRODUCTION or api_key["ENV"] == Env.PRODUCTION) else None
|
|
236
|
-
self._base_path = "
|
|
236
|
+
self._base_path = "https://api.sandbox.dana.id" if (api_key["DANA_ENV"] == Env.SANDBOX or api_key["ENV"] == Env.SANDBOX) else \
|
|
237
237
|
"https://api.saas.dana.id" if (api_key["DANA_ENV"] == Env.PRODUCTION or api_key["ENV"] == Env.PRODUCTION) else None
|
|
238
238
|
"""Default Base url
|
|
239
239
|
"""
|
|
@@ -595,7 +595,7 @@ class SnapConfiguration(BaseConfiguration):
|
|
|
595
595
|
|
|
596
596
|
host_settings = [
|
|
597
597
|
{
|
|
598
|
-
'url': "
|
|
598
|
+
'url': "https://api.sandbox.dana.id",
|
|
599
599
|
'description': "No description provided",
|
|
600
600
|
}
|
|
601
601
|
] if (self.api_key["DANA_ENV"] == Env.SANDBOX or self.api_key["ENV"] == Env.SANDBOX) else [
|
|
@@ -25,7 +25,7 @@ from cryptography.hazmat.primitives.asymmetric import padding
|
|
|
25
25
|
|
|
26
26
|
from dana.webhook.finish_notify_request import FinishNotifyRequest
|
|
27
27
|
|
|
28
|
-
# Sandbox gateway public key used for webhook signature verification when DANA_ENV/ENV is sandbox.
|
|
28
|
+
# Sandbox gateway public key used for webhook signature verification only when DANA_ENV/ENV is explicitly sandbox.
|
|
29
29
|
SANDBOX_WEBHOOK_PUBLIC_KEY = """-----BEGIN PUBLIC KEY-----
|
|
30
30
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnaKVGRbin4Wh4KN35OPh
|
|
31
31
|
ytJBjYTz7QZKSZjmHfiHxFmulfT87rta+IvGJ0rCBgg+1EtKk1hX8G5gPGJs1htJ
|
|
@@ -37,9 +37,41 @@ X6JV1OSST1C9sbPGzMHZ8AGB51BM0mok7davD/5irUk+f0C25OgzkwtxAt80dkDo
|
|
|
37
37
|
-----END PUBLIC KEY-----"""
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
def resolve_webhook_key_content(public_key=None, public_key_path=None) -> str:
|
|
41
|
+
"""
|
|
42
|
+
Selects the webhook public key content.
|
|
43
|
+
|
|
44
|
+
The sandbox key is used only when the environment is explicitly sandbox
|
|
45
|
+
(first non-empty of DANA_ENV, ENV, trimmed and lower-cased). In any other
|
|
46
|
+
case, including when neither variable is set, the supplied key is used:
|
|
47
|
+
public_key_path first, then public_key.
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
ValueError: If no key is available or the key file cannot be read.
|
|
51
|
+
"""
|
|
52
|
+
env = (os.getenv("DANA_ENV") or os.getenv("ENV") or "").strip().lower()
|
|
53
|
+
if env == "sandbox":
|
|
54
|
+
return SANDBOX_WEBHOOK_PUBLIC_KEY
|
|
55
|
+
if public_key_path:
|
|
56
|
+
try:
|
|
57
|
+
return Path(public_key_path).read_text().strip()
|
|
58
|
+
except Exception as e:
|
|
59
|
+
raise ValueError(f"Failed to read key from file path '{public_key_path}': {e}")
|
|
60
|
+
if public_key:
|
|
61
|
+
return public_key.strip()
|
|
62
|
+
raise ValueError(
|
|
63
|
+
"Either 'public_key' or 'public_key_path' must be provided. "
|
|
64
|
+
"In production, pass your DANA public key via 'public_key' or 'public_key_path'. "
|
|
65
|
+
"Set DANA_ENV=sandbox to use the built-in sandbox key."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
40
69
|
class WebhookParser:
|
|
41
70
|
"""
|
|
42
|
-
Verifies incoming webhook signatures and parse webhook request into FinishNotifyRequest object
|
|
71
|
+
Verifies incoming webhook signatures and parse webhook request into FinishNotifyRequest object.
|
|
72
|
+
|
|
73
|
+
The built-in sandbox key is used only when DANA_ENV (or ENV) is explicitly "sandbox";
|
|
74
|
+
otherwise the supplied public_key_path / public_key is required.
|
|
43
75
|
"""
|
|
44
76
|
def __init__(self, public_key: str = None, public_key_path: str = None):
|
|
45
77
|
"""
|
|
@@ -48,23 +80,13 @@ class WebhookParser:
|
|
|
48
80
|
public_key (str, optional): The public key as a string. Defaults to None.
|
|
49
81
|
public_key_path (str, optional): Path to the public key PEM file. Defaults to None.
|
|
50
82
|
If provided, this will be prioritized over public_key.
|
|
83
|
+
When DANA_ENV (or ENV) is "sandbox", the built-in sandbox key is used and both
|
|
84
|
+
arguments are ignored. For any other value, including unset, one of them is required.
|
|
51
85
|
Raises:
|
|
52
|
-
ValueError: If
|
|
86
|
+
ValueError: If no key is available (and the environment is not sandbox),
|
|
53
87
|
if the key file cannot be read, or if the key format is invalid.
|
|
54
88
|
"""
|
|
55
|
-
key_input_content =
|
|
56
|
-
env = (os.getenv("DANA_ENV") or os.getenv("ENV") or "").strip().lower()
|
|
57
|
-
if env == "sandbox" or env == "":
|
|
58
|
-
key_input_content = SANDBOX_WEBHOOK_PUBLIC_KEY
|
|
59
|
-
elif public_key_path:
|
|
60
|
-
try:
|
|
61
|
-
key_input_content = Path(public_key_path).read_text().strip()
|
|
62
|
-
except Exception as e:
|
|
63
|
-
raise ValueError(f"Failed to read key from file path '{public_key_path}': {e}")
|
|
64
|
-
elif public_key:
|
|
65
|
-
key_input_content = public_key.strip()
|
|
66
|
-
else:
|
|
67
|
-
raise ValueError("Either 'public_key' or 'public_key_path' must be provided.")
|
|
89
|
+
key_input_content = resolve_webhook_key_content(public_key, public_key_path)
|
|
68
90
|
|
|
69
91
|
if not key_input_content:
|
|
70
92
|
raise ValueError("Key content is empty.")
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# Copyright 2026 PT Espay Debit Indonesia Koe
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
16
|
+
# you may not use this file except in compliance with the License.
|
|
17
|
+
# You may obtain a copy of the License at
|
|
18
|
+
#
|
|
19
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
20
|
+
#
|
|
21
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
22
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
23
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
24
|
+
# See the License for the specific language governing permissions and
|
|
25
|
+
# limitations under the License.
|
|
26
|
+
|
|
27
|
+
import os
|
|
28
|
+
import uuid
|
|
29
|
+
import json
|
|
30
|
+
import base64
|
|
31
|
+
import hashlib
|
|
32
|
+
import urllib.parse
|
|
33
|
+
from datetime import datetime, timezone, timedelta
|
|
34
|
+
from typing import Optional, Dict, Any
|
|
35
|
+
from dana.widget.v1.models import WidgetPaymentResponse, ApplyOTTResponse
|
|
36
|
+
from dana.utils.snap_header import SnapHeader
|
|
37
|
+
|
|
38
|
+
# For cryptography operations
|
|
39
|
+
try:
|
|
40
|
+
from cryptography.hazmat.primitives.asymmetric import padding
|
|
41
|
+
from cryptography.hazmat.primitives import hashes
|
|
42
|
+
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
|
43
|
+
CRYPTOGRAPHY_AVAILABLE = True
|
|
44
|
+
except ImportError:
|
|
45
|
+
CRYPTOGRAPHY_AVAILABLE = False
|
|
46
|
+
|
|
47
|
+
# Import enum values - assuming these are defined in the generated code
|
|
48
|
+
try:
|
|
49
|
+
from dana.widget.v1.enum import Mode, TerminalType
|
|
50
|
+
except ImportError:
|
|
51
|
+
# If enums aren't available, define Mode as a simple class
|
|
52
|
+
class Mode:
|
|
53
|
+
API = "API"
|
|
54
|
+
DEEPLINK = "DEEPLINK"
|
|
55
|
+
|
|
56
|
+
class TerminalType:
|
|
57
|
+
WEB = "WEB"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class Util:
|
|
61
|
+
"""
|
|
62
|
+
Utility class for the Dana Widget API.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
_OAUTH_QUERY_ESCAPE_KEYS = frozenset(
|
|
66
|
+
{"redirectUrl", "timestamp", "seamlessData", "seamlessSign"}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
@staticmethod
|
|
70
|
+
def generate_channel_id() -> str:
|
|
71
|
+
"""Channel ID for widget OAuth URLs (max 5 chars). Matches Go GenerateChannelId."""
|
|
72
|
+
channel_id = os.environ.get("CHANNEL_ID") or "95221"
|
|
73
|
+
return channel_id[:5]
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def generate_scopes() -> str:
|
|
77
|
+
"""OAuth scopes based on environment. Matches Go GenerateScopes."""
|
|
78
|
+
env = os.environ.get("ENV") or os.environ.get("DANA_ENV") or "sandbox"
|
|
79
|
+
if env.lower() != "production":
|
|
80
|
+
return "CASHIER,AGREEMENT_PAY,QUERY_BALANCE,DEFAULT_BASIC_PROFILE,MINI_DANA"
|
|
81
|
+
return "MINI_DANA,CASHIER,QUERY_BALANCE,DEFAULT_BASIC_PROFILE"
|
|
82
|
+
|
|
83
|
+
@staticmethod
|
|
84
|
+
def generate_timestamp() -> str:
|
|
85
|
+
"""Timestamp in Asia/Jakarta RFC3339 format. Matches Go GenerateTimestamp."""
|
|
86
|
+
try:
|
|
87
|
+
from zoneinfo import ZoneInfo
|
|
88
|
+
|
|
89
|
+
now = datetime.now(ZoneInfo("Asia/Jakarta"))
|
|
90
|
+
except Exception:
|
|
91
|
+
now = datetime.now(timezone.utc) + timedelta(hours=7)
|
|
92
|
+
return now.strftime("%Y-%m-%dT%H:%M:%S+07:00")
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def _get_attr(data, name: str, default=None):
|
|
96
|
+
if isinstance(data, dict):
|
|
97
|
+
return data.get(name, default)
|
|
98
|
+
return getattr(data, name, default)
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def _is_field_set(data, field_name: str) -> bool:
|
|
102
|
+
fields_set = getattr(data, "model_fields_set", None)
|
|
103
|
+
if fields_set is None:
|
|
104
|
+
fields_set = getattr(data, "__fields_set__", None)
|
|
105
|
+
if fields_set is not None:
|
|
106
|
+
return field_name in fields_set
|
|
107
|
+
if isinstance(data, dict):
|
|
108
|
+
return field_name in data
|
|
109
|
+
return getattr(data, field_name, None) is not None
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def _normalize_seamless_data(seamless_data) -> Dict[str, Any]:
|
|
113
|
+
if seamless_data is None:
|
|
114
|
+
return {}
|
|
115
|
+
|
|
116
|
+
if isinstance(seamless_data, dict):
|
|
117
|
+
raw = dict(seamless_data)
|
|
118
|
+
elif hasattr(seamless_data, "model_dump"):
|
|
119
|
+
raw = seamless_data.model_dump(exclude_none=True)
|
|
120
|
+
elif hasattr(seamless_data, "dict"):
|
|
121
|
+
raw = seamless_data.dict(exclude_none=True)
|
|
122
|
+
else:
|
|
123
|
+
try:
|
|
124
|
+
raw = vars(seamless_data)
|
|
125
|
+
except TypeError:
|
|
126
|
+
raw = json.loads(json.dumps(seamless_data))
|
|
127
|
+
|
|
128
|
+
result = {
|
|
129
|
+
key: value
|
|
130
|
+
for key, value in raw.items()
|
|
131
|
+
if value is not None and value != ""
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if "mobileNumber" in result:
|
|
135
|
+
result["mobile"] = result.pop("mobileNumber")
|
|
136
|
+
if "mobile_number" in result:
|
|
137
|
+
result["mobile"] = result.pop("mobile_number")
|
|
138
|
+
|
|
139
|
+
return result
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
def _build_oauth_query_string(url_params: Dict[str, str]) -> str:
|
|
143
|
+
parts = []
|
|
144
|
+
for key, value in url_params.items():
|
|
145
|
+
if value is None:
|
|
146
|
+
continue
|
|
147
|
+
if key in Util._OAUTH_QUERY_ESCAPE_KEYS:
|
|
148
|
+
parts.append(f"{key}={urllib.parse.quote(str(value), safe='')}")
|
|
149
|
+
else:
|
|
150
|
+
parts.append(f"{key}={value}")
|
|
151
|
+
return "&".join(parts)
|
|
152
|
+
|
|
153
|
+
@staticmethod
|
|
154
|
+
def generate_oauth_url(data, private_key: Optional[str] = None, private_key_path: Optional[str] = None) -> str:
|
|
155
|
+
"""
|
|
156
|
+
Generate OAuth URL for testing
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
data: OAuth URL data object
|
|
160
|
+
private_key: Optional private key content
|
|
161
|
+
private_key_path: Optional path to private key file
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
str: The generated OAuth URL
|
|
165
|
+
"""
|
|
166
|
+
env = os.environ.get("DANA_ENV") or os.environ.get("ENV") or "sandbox"
|
|
167
|
+
mode = Util._get_attr(data, "mode") or Mode.API
|
|
168
|
+
|
|
169
|
+
if mode == Mode.DEEPLINK:
|
|
170
|
+
base_url = (
|
|
171
|
+
"https://m.dana.id/n/link/binding"
|
|
172
|
+
if env.lower() == "production"
|
|
173
|
+
else "https://m.sandbox.dana.id/n/link/binding"
|
|
174
|
+
)
|
|
175
|
+
else:
|
|
176
|
+
base_url = (
|
|
177
|
+
"https://m.dana.id/v1.0/get-auth-code"
|
|
178
|
+
if env.lower() == "production"
|
|
179
|
+
else "https://m.sandbox.dana.id/v1.0/get-auth-code"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
partner_id = os.environ.get("X_PARTNER_ID")
|
|
183
|
+
if not partner_id:
|
|
184
|
+
raise RuntimeError("X_PARTNER_ID is not defined")
|
|
185
|
+
|
|
186
|
+
state = Util._get_attr(data, "state")
|
|
187
|
+
if not state:
|
|
188
|
+
state = str(uuid.uuid4())
|
|
189
|
+
|
|
190
|
+
channel_id = Util.generate_channel_id()
|
|
191
|
+
|
|
192
|
+
scopes = Util._get_attr(data, "scopes")
|
|
193
|
+
if scopes:
|
|
194
|
+
scopes = ",".join(scopes) if isinstance(scopes, (list, tuple)) else scopes
|
|
195
|
+
else:
|
|
196
|
+
scopes = Util.generate_scopes()
|
|
197
|
+
|
|
198
|
+
external_id = Util._get_attr(data, "external_id")
|
|
199
|
+
if not external_id:
|
|
200
|
+
external_id = str(uuid.uuid4())
|
|
201
|
+
|
|
202
|
+
merchant_id = Util._get_attr(data, "merchant_id") or os.environ.get("MERCHANT_ID", "")
|
|
203
|
+
timestamp = Util.generate_timestamp()
|
|
204
|
+
request_id = None
|
|
205
|
+
|
|
206
|
+
if mode == Mode.DEEPLINK:
|
|
207
|
+
request_id = str(uuid.uuid4())
|
|
208
|
+
url_params = {
|
|
209
|
+
"partnerId": partner_id,
|
|
210
|
+
"scopes": scopes,
|
|
211
|
+
"terminalType": TerminalType.WEB,
|
|
212
|
+
"externalId": external_id,
|
|
213
|
+
"requestId": request_id,
|
|
214
|
+
"redirectUrl": Util._get_attr(data, "redirect_url"),
|
|
215
|
+
"state": state,
|
|
216
|
+
}
|
|
217
|
+
else:
|
|
218
|
+
url_params = {
|
|
219
|
+
"partnerId": partner_id,
|
|
220
|
+
"scopes": scopes,
|
|
221
|
+
"externalId": external_id,
|
|
222
|
+
"channelId": channel_id,
|
|
223
|
+
"redirectUrl": Util._get_attr(data, "redirect_url"),
|
|
224
|
+
"timestamp": timestamp,
|
|
225
|
+
"state": state,
|
|
226
|
+
"isSnapBI": "true",
|
|
227
|
+
}
|
|
228
|
+
if merchant_id:
|
|
229
|
+
url_params["merchantId"] = merchant_id
|
|
230
|
+
|
|
231
|
+
sub_merchant_id = Util._get_attr(data, "sub_merchant_id")
|
|
232
|
+
if sub_merchant_id:
|
|
233
|
+
url_params["subMerchantId"] = sub_merchant_id
|
|
234
|
+
|
|
235
|
+
if Util._is_field_set(data, "lang"):
|
|
236
|
+
lang = Util._get_attr(data, "lang")
|
|
237
|
+
if lang:
|
|
238
|
+
url_params["lang"] = lang
|
|
239
|
+
|
|
240
|
+
if Util._is_field_set(data, "allow_registration"):
|
|
241
|
+
allow_registration = Util._get_attr(data, "allow_registration")
|
|
242
|
+
if allow_registration is not None:
|
|
243
|
+
url_params["allowRegistration"] = (
|
|
244
|
+
"true" if allow_registration else "false"
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
seamless_data = Util._normalize_seamless_data(Util._get_attr(data, "seamless_data"))
|
|
248
|
+
if seamless_data:
|
|
249
|
+
if mode == Mode.DEEPLINK and request_id:
|
|
250
|
+
seamless_data["externalUid"] = external_id
|
|
251
|
+
seamless_data["reqTime"] = timestamp
|
|
252
|
+
seamless_data["verifiedTime"] = "0"
|
|
253
|
+
seamless_data["reqMsgId"] = request_id
|
|
254
|
+
|
|
255
|
+
seamless_data_json = json.dumps(seamless_data, separators=(",", ":"))
|
|
256
|
+
url_params["seamlessData"] = seamless_data_json
|
|
257
|
+
|
|
258
|
+
pk = private_key or os.environ.get("PRIVATE_KEY")
|
|
259
|
+
pk_path = private_key_path or os.environ.get("PRIVATE_KEY_PATH")
|
|
260
|
+
seamless_sign = Util.generate_seamless_sign(seamless_data, pk, pk_path)
|
|
261
|
+
if seamless_sign:
|
|
262
|
+
url_params["seamlessSign"] = seamless_sign
|
|
263
|
+
|
|
264
|
+
query_string = Util._build_oauth_query_string(url_params)
|
|
265
|
+
return f"{base_url}?{query_string}"
|
|
266
|
+
|
|
267
|
+
@staticmethod
|
|
268
|
+
def generate_seamless_sign(seamless_data: Dict[str, Any], private_key: Optional[str] = None, private_key_path: Optional[str] = None) -> str:
|
|
269
|
+
"""
|
|
270
|
+
Generate seamless sign for OAuth URL
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
seamless_data: The seamless data to sign
|
|
274
|
+
private_key: Optional private key content
|
|
275
|
+
private_key_path: Optional path to private key file
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
str: The generated signature
|
|
279
|
+
"""
|
|
280
|
+
try:
|
|
281
|
+
usable_private_key = SnapHeader.get_usable_private_key(private_key, private_key_path)
|
|
282
|
+
data_to_sign = json.dumps(seamless_data, separators=(",", ":")).encode()
|
|
283
|
+
|
|
284
|
+
if CRYPTOGRAPHY_AVAILABLE:
|
|
285
|
+
key = load_pem_private_key(usable_private_key.encode(), password=None)
|
|
286
|
+
signature = key.sign(
|
|
287
|
+
data_to_sign,
|
|
288
|
+
padding.PKCS1v15(),
|
|
289
|
+
hashes.SHA256(),
|
|
290
|
+
)
|
|
291
|
+
return base64.b64encode(signature).decode()
|
|
292
|
+
|
|
293
|
+
return hashlib.sha256(data_to_sign + str(int(datetime.now().timestamp())).encode()).hexdigest()
|
|
294
|
+
|
|
295
|
+
except Exception:
|
|
296
|
+
return hashlib.sha256(
|
|
297
|
+
(json.dumps(seamless_data, separators=(",", ":")) + str(int(datetime.now().timestamp()))).encode()
|
|
298
|
+
).hexdigest()
|
|
299
|
+
|
|
300
|
+
@staticmethod
|
|
301
|
+
def generate_complete_payment_url(widget_payment_response: WidgetPaymentResponse = None, apply_ott_response: ApplyOTTResponse = None) -> str:
|
|
302
|
+
"""
|
|
303
|
+
Combines the webRedirectUrl from WidgetPaymentResponse with the OTT token from ApplyOTTResponse
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
widget_payment_response: The widget payment response
|
|
307
|
+
apply_ott_response: The apply OTT response
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
str: The generated payment URL or empty string if inputs are invalid
|
|
311
|
+
"""
|
|
312
|
+
if widget_payment_response is None or apply_ott_response is None:
|
|
313
|
+
return ""
|
|
314
|
+
|
|
315
|
+
web_redirect_url = getattr(widget_payment_response, "web_redirect_url", None)
|
|
316
|
+
if not web_redirect_url:
|
|
317
|
+
return ""
|
|
318
|
+
|
|
319
|
+
user_resources = getattr(apply_ott_response, "user_resources", None)
|
|
320
|
+
if not user_resources or len(user_resources) == 0:
|
|
321
|
+
return web_redirect_url
|
|
322
|
+
|
|
323
|
+
ott_value = getattr(user_resources[0], "value", None) if user_resources else None
|
|
324
|
+
if not ott_value:
|
|
325
|
+
return web_redirect_url
|
|
326
|
+
|
|
327
|
+
parsed = urllib.parse.urlparse(web_redirect_url)
|
|
328
|
+
q = dict(urllib.parse.parse_qsl(parsed.query, keep_blank_values=True))
|
|
329
|
+
q["ott"] = str(ott_value)
|
|
330
|
+
new_query = urllib.parse.urlencode(q)
|
|
331
|
+
return urllib.parse.urlunparse((
|
|
332
|
+
parsed.scheme,
|
|
333
|
+
parsed.netloc,
|
|
334
|
+
parsed.path,
|
|
335
|
+
parsed.params,
|
|
336
|
+
new_query,
|
|
337
|
+
parsed.fragment,
|
|
338
|
+
))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dana-python
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: API Client (SDK) for DANA APIs based on https://dashboard.dana.id/api-docs
|
|
5
5
|
Author-email: DANA Package Manager <package-manager@dana.id>
|
|
6
6
|
Maintainer-email: DANA Package Manager <package-manager@dana.id>
|
|
@@ -8,19 +8,16 @@ License-Expression: Apache-2.0
|
|
|
8
8
|
Project-URL: homepage, https://dashboard.dana.id/api-docs
|
|
9
9
|
Project-URL: repository, https://github.com/dana-id/dana-python
|
|
10
10
|
Keywords: DANA,DANA ID Docs,DANA SDK,DANA Python,DANA API Client,DANA Python API Client,DANA Python SDK
|
|
11
|
-
Requires-Python:
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
-
Requires-Dist: annotated-types
|
|
15
|
-
Requires-Dist:
|
|
16
|
-
Requires-Dist: cryptography<46.0.0,>=44.0.2
|
|
17
|
-
Requires-Dist: pycparser==2.23
|
|
14
|
+
Requires-Dist: annotated-types>=0.7.0
|
|
15
|
+
Requires-Dist: cryptography>=50.0.0
|
|
18
16
|
Requires-Dist: pydantic<3.0.0,>=2.10.6
|
|
19
17
|
Requires-Dist: pydantic-core<3.0.0,>=2.27.2
|
|
20
|
-
Requires-Dist: python-dateutil
|
|
21
|
-
Requires-Dist: six==1.17.0
|
|
18
|
+
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
22
19
|
Requires-Dist: typing-extensions<5.0.0,>=4.12.2
|
|
23
|
-
Requires-Dist: urllib3<3.0.0,>=2.
|
|
20
|
+
Requires-Dist: urllib3<3.0.0,>=2.8.0
|
|
24
21
|
Dynamic: license-file
|
|
25
22
|
|
|
26
23
|
# dana-python
|
|
@@ -62,7 +59,7 @@ Here is the link: https://github.com/dana-id/uat-script.
|
|
|
62
59
|
|
|
63
60
|
## Requirements.
|
|
64
61
|
|
|
65
|
-
Python 3.9.1
|
|
62
|
+
Python 3.10+ (patched releases: 3.11.17, 3.12.15, 3.13.17, 3.14.9, 3.15.1 or later)
|
|
66
63
|
|
|
67
64
|
## Installation & Usage
|
|
68
65
|
### pip install
|
|
@@ -237,12 +237,15 @@ dana_python.egg-info/SOURCES.txt
|
|
|
237
237
|
dana_python.egg-info/dependency_links.txt
|
|
238
238
|
dana_python.egg-info/requires.txt
|
|
239
239
|
dana_python.egg-info/top_level.txt
|
|
240
|
+
tests/test_dependency_floors.py
|
|
240
241
|
tests/test_disbursement_api.py
|
|
241
242
|
tests/test_disbursement_custom_validation_enrich.py
|
|
242
243
|
tests/test_merchant_management_api.py
|
|
243
244
|
tests/test_payment_gateway_api.py
|
|
244
245
|
tests/test_payment_gateway_with_automation.py
|
|
246
|
+
tests/test_snap_configuration.py
|
|
245
247
|
tests/test_snap_header.py
|
|
246
248
|
tests/test_webhook.py
|
|
249
|
+
tests/test_webhook_key_selection.py
|
|
247
250
|
tests/test_widget_api.py
|
|
248
251
|
tests/test_widget_with_automation.py
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dana-python"
|
|
3
|
-
version = "
|
|
3
|
+
version = "3.0.0"
|
|
4
4
|
readme = "README.md"
|
|
5
5
|
description = "API Client (SDK) for DANA APIs based on https://dashboard.dana.id/api-docs"
|
|
6
6
|
license = "Apache-2.0"
|
|
7
|
-
requires-python = "
|
|
7
|
+
requires-python = ">=3.10"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name = "DANA Package Manager ", email = "package-manager@dana.id" },
|
|
10
10
|
]
|
|
@@ -13,16 +13,13 @@ maintainers = [
|
|
|
13
13
|
]
|
|
14
14
|
keywords = ["DANA", "DANA ID Docs", "DANA SDK", "DANA Python", "DANA API Client", "DANA Python API Client", "DANA Python SDK"]
|
|
15
15
|
dependencies = [
|
|
16
|
-
"annotated-types
|
|
17
|
-
"
|
|
18
|
-
"cryptography>=44.0.2,<46.0.0",
|
|
19
|
-
"pycparser==2.23",
|
|
16
|
+
"annotated-types>=0.7.0",
|
|
17
|
+
"cryptography>=50.0.0",
|
|
20
18
|
"pydantic>=2.10.6,<3.0.0",
|
|
21
19
|
"pydantic-core>=2.27.2,<3.0.0",
|
|
22
|
-
"python-dateutil
|
|
23
|
-
"six==1.17.0",
|
|
20
|
+
"python-dateutil>=2.9.0.post0",
|
|
24
21
|
"typing-extensions>=4.12.2,<5.0.0",
|
|
25
|
-
"urllib3>=2.
|
|
22
|
+
"urllib3>=2.8.0,<3.0.0"
|
|
26
23
|
]
|
|
27
24
|
|
|
28
25
|
[project.urls]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from importlib.metadata import version
|
|
2
|
+
try:
|
|
3
|
+
import tomllib # 3.11+
|
|
4
|
+
except ModuleNotFoundError: # Python 3.10
|
|
5
|
+
import tomli as tomllib
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
PYPROJECT = tomllib.loads((Path(__file__).parent.parent / "pyproject.toml").read_text())
|
|
9
|
+
|
|
10
|
+
def _major_minor(v):
|
|
11
|
+
return tuple(int(p) for p in v.split(".")[:2])
|
|
12
|
+
|
|
13
|
+
def test_requires_python_floor():
|
|
14
|
+
assert PYPROJECT["project"]["requires-python"] == ">=3.10"
|
|
15
|
+
|
|
16
|
+
def test_cryptography_spec_has_floor_and_no_cap():
|
|
17
|
+
spec = next(d for d in PYPROJECT["project"]["dependencies"] if d.startswith("cryptography"))
|
|
18
|
+
assert spec == "cryptography>=50.0.0"
|
|
19
|
+
|
|
20
|
+
def test_urllib3_spec():
|
|
21
|
+
assert "urllib3>=2.8.0,<3.0.0" in PYPROJECT["project"]["dependencies"]
|
|
22
|
+
|
|
23
|
+
def test_installed_versions_are_patched():
|
|
24
|
+
assert _major_minor(version("cryptography")) >= (50, 0)
|
|
25
|
+
assert _major_minor(version("urllib3")) >= (2, 8)
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
import json
|
|
15
|
+
import os
|
|
16
|
+
|
|
15
17
|
import pytest
|
|
16
18
|
from dana.payment_gateway.v1 import PaymentGatewayApi
|
|
17
19
|
from dana.payment_gateway.v1.models import ConsultPayPaymentInfo, ConsultPayRequest, CreateOrderByApiRequest, CreateOrderByRedirectRequest, QueryPaymentRequest, CancelOrderRequest, RefundOrderRequest, Money, PayOptionDetail
|
|
@@ -21,6 +23,7 @@ from dana.webhook import WebhookParser
|
|
|
21
23
|
from dana.utils.snap_header import SnapHeader
|
|
22
24
|
# Import fixtures directly from their modules to avoid circular imports
|
|
23
25
|
from tests.fixtures.api_client import api_instance_payment_gateway, api_instance_payment_gateway_with_key_path
|
|
26
|
+
from tests.helpers.env_credentials import external_shop_id
|
|
24
27
|
from tests.fixtures.payment_gateway import consult_pay_request, consult_pay_request_without_buyer, consult_pay_request_with_external_store_id, create_order_by_api_request, query_payment_request, cancel_order_request, refund_order_request, webhook_key_pair
|
|
25
28
|
|
|
26
29
|
|
|
@@ -51,6 +54,8 @@ class TestPaymentGatewayApi:
|
|
|
51
54
|
|
|
52
55
|
def test_consult_pay_with_external_store_id(self, api_instance_payment_gateway: PaymentGatewayApi, consult_pay_request_with_external_store_id: ConsultPayRequest):
|
|
53
56
|
"""Should give success response code and message and correct mandatory fields"""
|
|
57
|
+
if not external_shop_id():
|
|
58
|
+
pytest.skip("EXTERNAL_SHOP_ID is required for consult pay with external store id")
|
|
54
59
|
|
|
55
60
|
api_response = api_instance_payment_gateway.consult_pay(consult_pay_request_with_external_store_id)
|
|
56
61
|
|
|
@@ -80,6 +85,10 @@ class TestPaymentGatewayApi:
|
|
|
80
85
|
assert api_response.response_code == '2005400'
|
|
81
86
|
assert api_response.response_message == 'Successful'
|
|
82
87
|
|
|
88
|
+
@pytest.mark.skipif(
|
|
89
|
+
"CI" in os.environ or "GITLAB_CI" in os.environ,
|
|
90
|
+
reason="CI: sandbox merchant lacks cancel permission (4035715 / 5005701)",
|
|
91
|
+
)
|
|
83
92
|
def test_cancel_order_success(self, api_instance_payment_gateway: PaymentGatewayApi, create_order_by_api_request: CreateOrderByApiRequest, cancel_order_request: CancelOrderRequest):
|
|
84
93
|
"""Should successfully cancel an order and return success response code"""
|
|
85
94
|
|
|
@@ -642,8 +651,12 @@ class TestPaymentGatewaySandboxPayMethodPayOptionValidation:
|
|
|
642
651
|
):
|
|
643
652
|
from tests.fixtures.payment_gateway import PaymentGatewayFixtures
|
|
644
653
|
|
|
654
|
+
shop_id = external_shop_id()
|
|
655
|
+
if not shop_id:
|
|
656
|
+
pytest.skip("EXTERNAL_SHOP_ID is required for create order with external store id")
|
|
657
|
+
|
|
645
658
|
request = PaymentGatewayFixtures.get_create_order_by_redirect_request()
|
|
646
|
-
request.external_store_id =
|
|
659
|
+
request.external_store_id = shop_id
|
|
647
660
|
response = api_instance_payment_gateway.create_order(request)
|
|
648
661
|
|
|
649
662
|
if response.response_message:
|