dana-python 2.0.0__tar.gz → 2.1.1__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.0.0/dana_python.egg-info → dana_python-2.1.1}/PKG-INFO +4 -4
- {dana_python-2.0.0 → dana_python-2.1.1}/README.md +1 -1
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/exceptions.py +5 -1
- dana_python-2.1.1/dana/payment_gateway/v1/custom_validation.py +333 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/rest.py +6 -1
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/utils/open_api_header.py +33 -8
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/utils/script.py +16 -1
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/api/widget_api.py +65 -0
- dana_python-2.1.1/dana/widget/v1/custom_validation.py +112 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/enum.py +11 -2
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/account_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/account_unbinding_request.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/account_unbinding_request_additional_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/account_unbinding_response.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/amount_detail.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/apply_ott_request.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/apply_ott_request_additional_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/apply_ott_response.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/apply_ott_response_user_resources_inner.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/apply_token_authorization_code_request.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/apply_token_refresh_token_request.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/apply_token_response.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/apply_token_response_additional_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/apply_token_response_additional_info_user_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/balance_inquiry_request.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/balance_inquiry_request_additional_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/balance_inquiry_response.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/buyer.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/cancel_order_request.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/cancel_order_response.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/env_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/goods.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/international_order_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/international_order_info_exchange_rate.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/money.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/oauth2_url_data.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/oauth2_url_data_seamless_data.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/order.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/pay_option_detail.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/pay_option_detail_additional_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/pay_option_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/payment_promo_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/payment_view.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/query_payment_request.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/query_payment_response.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/query_payment_response_additional_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/query_user_profile_request.py +16 -7
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/query_user_profile_response.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/query_user_profile_response_response.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/query_user_profile_response_response_body.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/query_user_profile_response_response_head.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/refund_order_request.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/refund_order_request_additional_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/refund_order_response.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/refund_promo_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/result_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/seller.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/service_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/shipping_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/status_detail.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/time_detail.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/url_param.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/user_resource_info.py +12 -5
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/virtual_account_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/widget_payment_request.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/widget_payment_request_additional_info.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/widget_payment_response.py +10 -3
- {dana_python-2.0.0 → dana_python-2.1.1/dana_python.egg-info}/PKG-INFO +4 -4
- {dana_python-2.0.0 → dana_python-2.1.1}/dana_python.egg-info/requires.txt +2 -2
- {dana_python-2.0.0 → dana_python-2.1.1}/pyproject.toml +4 -4
- {dana_python-2.0.0 → dana_python-2.1.1}/tests/test_payment_gateway_api.py +216 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/tests/test_widget_with_automation.py +180 -0
- dana_python-2.0.0/dana/payment_gateway/v1/custom_validation.py +0 -184
- dana_python-2.0.0/dana/widget/v1/custom_validation.py +0 -80
- {dana_python-2.0.0 → dana_python-2.1.1}/LICENSE +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/api_client.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/api_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/base/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/base/configuration.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/base/model.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/base/types.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/api/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/api/disbursement_api.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/enum.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/bank_account_inquiry_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/bank_account_inquiry_request_additional_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/bank_account_inquiry_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/bank_account_inquiry_response_additional_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/dana_account_inquiry_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/dana_account_inquiry_request_additional_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/dana_account_inquiry_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/money.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/transfer_to_bank_inquiry_status_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/transfer_to_bank_inquiry_status_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/transfer_to_bank_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/transfer_to_bank_request_additional_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/transfer_to_bank_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/transfer_to_dana_inquiry_status_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/transfer_to_dana_inquiry_status_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/transfer_to_dana_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/transfer_to_dana_request_additional_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/disbursement/v1/models/transfer_to_dana_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/api/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/api/merchant_management_api.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/enum.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/address_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/business_docs.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/create_division_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/create_division_request_ext_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/create_division_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/create_division_response_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/create_division_response_response_body.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/create_division_response_response_head.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/create_shop_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/create_shop_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/create_shop_response_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/create_shop_response_response_body.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/create_shop_response_response_head.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/division_resource_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/merchant_resource_information.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/mobile_no_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/pic_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_division_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_division_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_division_response_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_division_response_response_body.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_division_response_response_head.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_merchant_resource_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_merchant_resource_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_merchant_resource_response_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_merchant_resource_response_response_body.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_merchant_resource_response_response_head.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_shop_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_shop_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_shop_response_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_shop_response_response_body.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/query_shop_response_response_head.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/result_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/shop_resource_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/update_division_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/update_division_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/update_division_response_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/update_division_response_response_body.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/update_division_response_response_head.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/update_shop_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/update_shop_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/update_shop_response_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/update_shop_response_response_head.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/merchant_management/v1/models/user_name.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/api/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/api/payment_gateway_api.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/enum.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/actor_context.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/amount_detail.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/audit_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/buyer.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/cancel_order_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/cancel_order_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/consult_pay_payment_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/consult_pay_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/consult_pay_request_additional_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/consult_pay_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/create_order_by_api_additional_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/create_order_by_api_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/create_order_by_redirect_additional_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/create_order_by_redirect_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/create_order_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/create_order_response_additional_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/env_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/goods.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/money.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/order_api_object.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/order_redirect_object.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/pay_option_additional_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/pay_option_detail.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/pay_option_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/payment_view.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/promo_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/query_payment_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/query_payment_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/query_payment_response_additional_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/refund_option_bill.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/refund_order_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/refund_order_request_additional_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/refund_order_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/seller.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/shipping_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/status_detail.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/time_detail.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/url_param.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/payment_gateway/v1/models/virtual_account_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/utils/date_validation.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/utils/models.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/utils/open_api_configuration.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/utils/snap_configuration.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/utils/snap_header.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/utils/url.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/webhook/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/webhook/finish_notify_payment_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/webhook/finish_notify_request.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/webhook/finish_notify_request_additional_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/webhook/finish_notify_response.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/webhook/money.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/webhook/pay_option_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/webhook/shop_info.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/webhook/webhook.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/api/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/models/__init__.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana/widget/v1/util.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana_python.egg-info/SOURCES.txt +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana_python.egg-info/dependency_links.txt +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/dana_python.egg-info/top_level.txt +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/setup.cfg +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/tests/test_disbursement_api.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/tests/test_merchant_management_api.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/tests/test_payment_gateway_with_automation.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/tests/test_snap_header.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/tests/test_webhook.py +0 -0
- {dana_python-2.0.0 → dana_python-2.1.1}/tests/test_widget_api.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dana-python
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
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>
|
|
@@ -12,9 +12,9 @@ Requires-Python: >3.9.1
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
Requires-Dist: annotated-types==0.7.0
|
|
15
|
-
Requires-Dist: cffi==
|
|
15
|
+
Requires-Dist: cffi==2.0.0
|
|
16
16
|
Requires-Dist: cryptography<46.0.0,>=44.0.2
|
|
17
|
-
Requires-Dist: pycparser==2.
|
|
17
|
+
Requires-Dist: pycparser==2.23
|
|
18
18
|
Requires-Dist: pydantic<3.0.0,>=2.10.6
|
|
19
19
|
Requires-Dist: pydantic-core<3.0.0,>=2.27.2
|
|
20
20
|
Requires-Dist: python-dateutil==2.9.0.post0
|
|
@@ -88,7 +88,7 @@ Before using the SDK, please make sure to set the following environment variable
|
|
|
88
88
|
| ---------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
|
89
89
|
| `ENV` or `DANA_ENV` | Defines which environment the SDK will use. Possible values: `SANDBOX` or `PRODUCTION`. | `SANDBOX` |
|
|
90
90
|
| `X_PARTNER_ID` | Unique identifier for partner, provided by DANA, also known as `clientId`. | 1970010100000000000000 |
|
|
91
|
-
| `PRIVATE_KEY` | Your private key string. |
|
|
91
|
+
| `PRIVATE_KEY` | Your private key string. | |
|
|
92
92
|
| `PRIVATE_KEY_PATH` | Path to your private key file. If both are set, `PRIVATE_KEY_PATH` is used. | /path/to/your_private_key.pem |
|
|
93
93
|
| `DANA_PUBLIC_KEY` | DANA public key string for parsing webhook. | `-----BEGIN PUBLIC KEY-----MIIBIjANBgkq...Do/QIDAQAB-----END PUBLIC KEY-----` |
|
|
94
94
|
| `DANA_PUBLIC_KEY_PATH` | Path to DANA public key file for parsing webhook. If both set, `DANA_PUBLIC_KEY_PATH is used. | /path/to/dana_public_key.pem |
|
|
@@ -63,7 +63,7 @@ Before using the SDK, please make sure to set the following environment variable
|
|
|
63
63
|
| ---------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
|
64
64
|
| `ENV` or `DANA_ENV` | Defines which environment the SDK will use. Possible values: `SANDBOX` or `PRODUCTION`. | `SANDBOX` |
|
|
65
65
|
| `X_PARTNER_ID` | Unique identifier for partner, provided by DANA, also known as `clientId`. | 1970010100000000000000 |
|
|
66
|
-
| `PRIVATE_KEY` | Your private key string. |
|
|
66
|
+
| `PRIVATE_KEY` | Your private key string. | |
|
|
67
67
|
| `PRIVATE_KEY_PATH` | Path to your private key file. If both are set, `PRIVATE_KEY_PATH` is used. | /path/to/your_private_key.pem |
|
|
68
68
|
| `DANA_PUBLIC_KEY` | DANA public key string for parsing webhook. | `-----BEGIN PUBLIC KEY-----MIIBIjANBgkq...Do/QIDAQAB-----END PUBLIC KEY-----` |
|
|
69
69
|
| `DANA_PUBLIC_KEY_PATH` | Path to DANA public key file for parsing webhook. If both set, `DANA_PUBLIC_KEY_PATH is used. | /path/to/dana_public_key.pem |
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
Do not edit the class manually.
|
|
23
23
|
""" # noqa: E501
|
|
24
24
|
|
|
25
|
-
from typing import Any, Optional
|
|
25
|
+
from typing import Any, Dict, List, Optional
|
|
26
26
|
from typing_extensions import Self
|
|
27
27
|
|
|
28
28
|
class OpenApiException(Exception):
|
|
@@ -122,7 +122,11 @@ class ApiException(OpenApiException):
|
|
|
122
122
|
*,
|
|
123
123
|
body: Optional[str] = None,
|
|
124
124
|
data: Optional[Any] = None,
|
|
125
|
+
contexts: Optional[List[Dict[str, str]]] = None,
|
|
125
126
|
) -> None:
|
|
127
|
+
self.contexts = contexts
|
|
128
|
+
if contexts is not None and reason is None:
|
|
129
|
+
reason = "; ".join(f"{c['field']}: {c['message']}" for c in contexts)
|
|
126
130
|
self.status = status
|
|
127
131
|
self.reason = reason
|
|
128
132
|
self.body = body
|
|
@@ -0,0 +1,333 @@
|
|
|
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
|
+
"""
|
|
16
|
+
CustomValidation
|
|
17
|
+
|
|
18
|
+
This module provides custom validation functions for Payment Gateway API requests.
|
|
19
|
+
Validations are registered in the validation_registry and executed via custom_validation().
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import os
|
|
23
|
+
import re
|
|
24
|
+
from typing import Any, Callable, Dict, FrozenSet, List, Set
|
|
25
|
+
|
|
26
|
+
from dana.utils.date_validation import validate_valid_up_to_date
|
|
27
|
+
from dana.exceptions import ApiException
|
|
28
|
+
|
|
29
|
+
# Money value pattern: digits (1-16) + "." + exactly 2 digits (e.g. 10000.00)
|
|
30
|
+
MONEY_VALUE_PATTERN = re.compile(r'^\d{1,16}\.\d{2}$')
|
|
31
|
+
|
|
32
|
+
SANDBOX_ALLOWED_PAY_METHODS: FrozenSet[str] = frozenset({
|
|
33
|
+
'BALANCE', 'CREDIT_CARD', 'DEBIT_CARD', 'VIRTUAL_ACCOUNT', 'NETWORK_PAY',
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
SANDBOX_ALLOWED_PAY_OPTIONS: FrozenSet[str] = frozenset({
|
|
37
|
+
'CARD', 'QRIS', 'BRI', 'PANIN', 'CIMB', 'MANDIRI', 'BTPN',
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
CREDIT_DEBIT_CARD_PAY_METHODS: Set[str] = {'CREDIT_CARD', 'DEBIT_CARD'}
|
|
41
|
+
NETWORK_PAY_PG_CARD = 'NETWORK_PAY_PG_CARD'
|
|
42
|
+
EWALLET_PAY_OPTIONS: Set[str] = {
|
|
43
|
+
'NETWORK_PAY_PG_SPAY',
|
|
44
|
+
'NETWORK_PAY_PG_OVO',
|
|
45
|
+
'NETWORK_PAY_PG_GOPAY',
|
|
46
|
+
'NETWORK_PAY_PG_LINKAJA',
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _is_sandbox() -> bool:
|
|
51
|
+
env = os.getenv('DANA_ENV', os.getenv('ENV', 'sandbox')).lower()
|
|
52
|
+
return env == 'sandbox'
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _pay_option_allowed_in_sandbox(value: str) -> bool:
|
|
56
|
+
if not value or not str(value).strip():
|
|
57
|
+
return False
|
|
58
|
+
s = str(value).strip()
|
|
59
|
+
if s in SANDBOX_ALLOWED_PAY_OPTIONS:
|
|
60
|
+
return True
|
|
61
|
+
for opt in SANDBOX_ALLOWED_PAY_OPTIONS:
|
|
62
|
+
if s.endswith('_' + opt):
|
|
63
|
+
return True
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _normalize_value(value: Any) -> str:
|
|
68
|
+
if value is None:
|
|
69
|
+
return ''
|
|
70
|
+
if hasattr(value, 'value'):
|
|
71
|
+
return str(getattr(value, 'value')).strip()
|
|
72
|
+
return str(value).strip()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _trim_str(value: Any) -> str:
|
|
76
|
+
if value is None:
|
|
77
|
+
return ''
|
|
78
|
+
return str(value).strip()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _rune_len(s: str) -> int:
|
|
82
|
+
return len(list(s))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _ctx(field: str, message: str) -> Dict[str, str]:
|
|
86
|
+
return {'field': field, 'message': message}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def validate_additional_info_required(request: Any) -> None:
|
|
90
|
+
if request is None:
|
|
91
|
+
return
|
|
92
|
+
if hasattr(request, 'additional_info') and request.additional_info is None:
|
|
93
|
+
raise ApiException(status=0, contexts=[_ctx('additionalInfo', 'additionalInfo is required')])
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def validate_money_value_pattern(request: Any) -> None:
|
|
97
|
+
if request is None:
|
|
98
|
+
return
|
|
99
|
+
if not hasattr(request, 'amount') or request.amount is None:
|
|
100
|
+
return
|
|
101
|
+
value = getattr(request.amount, 'value', None)
|
|
102
|
+
if value is None or value == '':
|
|
103
|
+
raise ApiException(status=0, contexts=[_ctx('amount.value', 'amount.value is required')])
|
|
104
|
+
if not MONEY_VALUE_PATTERN.match(str(value)):
|
|
105
|
+
raise ApiException(status=0, contexts=[
|
|
106
|
+
_ctx('amount.value', f'amount.value must match pattern (e.g. 10000.00): got {value!r}')
|
|
107
|
+
])
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def validate_valid_up_to_create_order_request(request: Any) -> None:
|
|
111
|
+
if request is None:
|
|
112
|
+
return
|
|
113
|
+
if hasattr(request, 'valid_up_to') and request.valid_up_to is not None:
|
|
114
|
+
try:
|
|
115
|
+
validate_valid_up_to_date(request.valid_up_to)
|
|
116
|
+
except ValueError as e:
|
|
117
|
+
raise ApiException(status=0, contexts=[
|
|
118
|
+
_ctx('validUpTo', f'validUpTo validation failed: {str(e)}')
|
|
119
|
+
]) from e
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def validate_external_store_id_for_qris(request: Any) -> None:
|
|
123
|
+
if request is None:
|
|
124
|
+
return
|
|
125
|
+
if not hasattr(request, 'pay_option_details') or request.pay_option_details is None:
|
|
126
|
+
return
|
|
127
|
+
has_qris = False
|
|
128
|
+
if isinstance(request.pay_option_details, list):
|
|
129
|
+
for pay_option_detail in request.pay_option_details:
|
|
130
|
+
if hasattr(pay_option_detail, 'pay_option') and pay_option_detail.pay_option == 'NETWORK_PAY_PG_QRIS':
|
|
131
|
+
has_qris = True
|
|
132
|
+
break
|
|
133
|
+
if has_qris:
|
|
134
|
+
external_store_id = None
|
|
135
|
+
if hasattr(request, 'external_store_id'):
|
|
136
|
+
external_store_id = request.external_store_id
|
|
137
|
+
if not external_store_id or (isinstance(external_store_id, str) and external_store_id.strip() == ''):
|
|
138
|
+
raise ApiException(status=0, contexts=[
|
|
139
|
+
_ctx('externalStoreId', 'externalStoreId is required when payOption is NETWORK_PAY_PG_QRIS')
|
|
140
|
+
])
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def validate_sandbox_pay_method_and_pay_option(request: Any) -> None:
|
|
144
|
+
if request is None or not _is_sandbox():
|
|
145
|
+
return
|
|
146
|
+
pay_option_details = getattr(request, 'pay_option_details', None)
|
|
147
|
+
if not pay_option_details or not isinstance(pay_option_details, list):
|
|
148
|
+
return
|
|
149
|
+
for i, detail in enumerate(pay_option_details):
|
|
150
|
+
if not detail:
|
|
151
|
+
continue
|
|
152
|
+
if hasattr(detail, 'pay_method') and detail.pay_method is not None:
|
|
153
|
+
pm_str = _normalize_value(detail.pay_method)
|
|
154
|
+
if pm_str and pm_str not in SANDBOX_ALLOWED_PAY_METHODS:
|
|
155
|
+
raise ApiException(status=0, contexts=[
|
|
156
|
+
_ctx(
|
|
157
|
+
f'payOptionDetails[{i}].payMethod',
|
|
158
|
+
(
|
|
159
|
+
f'In sandbox, payMethod must be one of [{", ".join(sorted(SANDBOX_ALLOWED_PAY_METHODS))}]; '
|
|
160
|
+
f'got {pm_str}'
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
])
|
|
164
|
+
if hasattr(detail, 'pay_option') and detail.pay_option is not None:
|
|
165
|
+
po_str = _normalize_value(detail.pay_option)
|
|
166
|
+
if po_str and not _pay_option_allowed_in_sandbox(po_str):
|
|
167
|
+
raise ApiException(status=0, contexts=[
|
|
168
|
+
_ctx(
|
|
169
|
+
f'payOptionDetails[{i}].payOption',
|
|
170
|
+
(
|
|
171
|
+
f'In sandbox, payOption must be one of [{", ".join(sorted(SANDBOX_ALLOWED_PAY_OPTIONS))}] '
|
|
172
|
+
f'(or suffix like VIRTUAL_ACCOUNT_BRI); got {po_str}'
|
|
173
|
+
),
|
|
174
|
+
)
|
|
175
|
+
])
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def validate_conditional_pay_option_additional_info_create_order_request(request: Any) -> None:
|
|
179
|
+
if request is None:
|
|
180
|
+
return
|
|
181
|
+
pay_option_details = getattr(request, 'pay_option_details', None)
|
|
182
|
+
if not pay_option_details or not isinstance(pay_option_details, list):
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
contexts: List[Dict[str, str]] = []
|
|
186
|
+
|
|
187
|
+
for i, detail in enumerate(pay_option_details):
|
|
188
|
+
if not detail:
|
|
189
|
+
continue
|
|
190
|
+
pay_method = _trim_str(getattr(detail, 'pay_method', None))
|
|
191
|
+
pay_option = _trim_str(getattr(detail, 'pay_option', None))
|
|
192
|
+
additional_info = getattr(detail, 'additional_info', None)
|
|
193
|
+
phone_raw = None
|
|
194
|
+
if additional_info is not None:
|
|
195
|
+
phone_raw = getattr(additional_info, 'phone_number', None)
|
|
196
|
+
phone_number = _trim_str(phone_raw)
|
|
197
|
+
|
|
198
|
+
is_card = pay_method in CREDIT_DEBIT_CARD_PAY_METHODS or pay_option == NETWORK_PAY_PG_CARD
|
|
199
|
+
is_ewallet = pay_option in EWALLET_PAY_OPTIONS
|
|
200
|
+
|
|
201
|
+
if is_card or is_ewallet:
|
|
202
|
+
field = f'payOptionDetails[{i}].additionalInfo.phoneNumber'
|
|
203
|
+
if not phone_number:
|
|
204
|
+
contexts.append(
|
|
205
|
+
_ctx(field, f'phoneNumber is required for card/e-wallet payment (payOptionDetails[{i}])')
|
|
206
|
+
)
|
|
207
|
+
else:
|
|
208
|
+
ln = _rune_len(phone_number)
|
|
209
|
+
if ln < 1 or ln > 15:
|
|
210
|
+
contexts.append(
|
|
211
|
+
_ctx(field, f'phoneNumber must be between 1 and 15 characters (payOptionDetails[{i}])')
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
if contexts:
|
|
215
|
+
raise ApiException(status=0, contexts=contexts)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def validate_optional_fields_with_required_nested_create_order_request(request: Any) -> None:
|
|
219
|
+
if request is None:
|
|
220
|
+
return
|
|
221
|
+
additional_info = getattr(request, 'additional_info', None)
|
|
222
|
+
if additional_info is None:
|
|
223
|
+
return
|
|
224
|
+
order = getattr(additional_info, 'order', None)
|
|
225
|
+
if not order:
|
|
226
|
+
return
|
|
227
|
+
|
|
228
|
+
contexts: List[Dict[str, str]] = []
|
|
229
|
+
|
|
230
|
+
buyer = getattr(order, 'buyer', None)
|
|
231
|
+
if buyer:
|
|
232
|
+
ext_type = _trim_str(getattr(buyer, 'external_user_type', None))
|
|
233
|
+
ext_id = _trim_str(getattr(buyer, 'external_user_id', None))
|
|
234
|
+
has_type = bool(ext_type)
|
|
235
|
+
has_id = bool(ext_id)
|
|
236
|
+
if has_id and not has_type:
|
|
237
|
+
contexts.append(
|
|
238
|
+
_ctx(
|
|
239
|
+
'additionalInfo.order.buyer.externalUserType',
|
|
240
|
+
'externalUserType is required when externalUserId is filled',
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
if has_type and not has_id:
|
|
244
|
+
contexts.append(
|
|
245
|
+
_ctx(
|
|
246
|
+
'additionalInfo.order.buyer.externalUserId',
|
|
247
|
+
'externalUserId is required when externalUserType is filled',
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
goods = getattr(order, 'goods', None)
|
|
252
|
+
if isinstance(goods, list) and len(goods) > 0:
|
|
253
|
+
for i, g in enumerate(goods):
|
|
254
|
+
if not g:
|
|
255
|
+
continue
|
|
256
|
+
name = _trim_str(getattr(g, 'name', None))
|
|
257
|
+
if not name:
|
|
258
|
+
contexts.append(
|
|
259
|
+
_ctx(
|
|
260
|
+
f'additionalInfo.order.goods[{i}].name',
|
|
261
|
+
'name is required when goods is filled',
|
|
262
|
+
)
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
shipping_info = getattr(order, 'shipping_info', None)
|
|
266
|
+
if isinstance(shipping_info, list) and len(shipping_info) > 0:
|
|
267
|
+
for i, s in enumerate(shipping_info):
|
|
268
|
+
if not s:
|
|
269
|
+
continue
|
|
270
|
+
first_name = _trim_str(getattr(s, 'first_name', None))
|
|
271
|
+
if not first_name:
|
|
272
|
+
contexts.append(
|
|
273
|
+
_ctx(
|
|
274
|
+
f'additionalInfo.order.shippingInfo[{i}].firstName',
|
|
275
|
+
'firstName is required when shippingInfo is filled',
|
|
276
|
+
)
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
if contexts:
|
|
280
|
+
raise ApiException(status=0, contexts=contexts)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
validation_registry: Dict[str, List[Callable[[Any], None]]] = {
|
|
284
|
+
'CreateOrderByApiRequest': [
|
|
285
|
+
validate_additional_info_required,
|
|
286
|
+
validate_money_value_pattern,
|
|
287
|
+
validate_valid_up_to_create_order_request,
|
|
288
|
+
validate_external_store_id_for_qris,
|
|
289
|
+
validate_sandbox_pay_method_and_pay_option,
|
|
290
|
+
validate_conditional_pay_option_additional_info_create_order_request,
|
|
291
|
+
validate_optional_fields_with_required_nested_create_order_request,
|
|
292
|
+
],
|
|
293
|
+
'CreateOrderByRedirectRequest': [
|
|
294
|
+
validate_additional_info_required,
|
|
295
|
+
validate_money_value_pattern,
|
|
296
|
+
validate_valid_up_to_create_order_request,
|
|
297
|
+
validate_sandbox_pay_method_and_pay_option,
|
|
298
|
+
validate_conditional_pay_option_additional_info_create_order_request,
|
|
299
|
+
validate_optional_fields_with_required_nested_create_order_request,
|
|
300
|
+
],
|
|
301
|
+
'CreateOrderRequest': [
|
|
302
|
+
validate_additional_info_required,
|
|
303
|
+
validate_money_value_pattern,
|
|
304
|
+
validate_valid_up_to_create_order_request,
|
|
305
|
+
validate_external_store_id_for_qris,
|
|
306
|
+
validate_sandbox_pay_method_and_pay_option,
|
|
307
|
+
validate_conditional_pay_option_additional_info_create_order_request,
|
|
308
|
+
validate_optional_fields_with_required_nested_create_order_request,
|
|
309
|
+
],
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def custom_validation(request: Any) -> None:
|
|
314
|
+
"""Run all validators for the request type and aggregate client validation contexts."""
|
|
315
|
+
if request is None:
|
|
316
|
+
return
|
|
317
|
+
|
|
318
|
+
class_name = request.__class__.__name__
|
|
319
|
+
if class_name not in validation_registry:
|
|
320
|
+
return
|
|
321
|
+
|
|
322
|
+
aggregated: List[Dict[str, str]] = []
|
|
323
|
+
for validator in validation_registry[class_name]:
|
|
324
|
+
try:
|
|
325
|
+
validator(request)
|
|
326
|
+
except ApiException as e:
|
|
327
|
+
if e.contexts:
|
|
328
|
+
aggregated.extend(e.contexts)
|
|
329
|
+
else:
|
|
330
|
+
raise
|
|
331
|
+
|
|
332
|
+
if aggregated:
|
|
333
|
+
raise ApiException(status=0, contexts=aggregated)
|
|
@@ -190,7 +190,12 @@ class RESTClientObject:
|
|
|
190
190
|
):
|
|
191
191
|
request_body = None
|
|
192
192
|
if body is not None:
|
|
193
|
-
|
|
193
|
+
# If body is already a string or bytes (e.g. pre-serialized JSON from OpenAPI widget),
|
|
194
|
+
# send as-is to avoid double JSON encoding. Otherwise serialize.
|
|
195
|
+
if isinstance(body, (str, bytes)):
|
|
196
|
+
request_body = body
|
|
197
|
+
else:
|
|
198
|
+
request_body = json.dumps(body)
|
|
194
199
|
r = self.pool_manager.request(
|
|
195
200
|
method,
|
|
196
201
|
url,
|
|
@@ -90,12 +90,37 @@ class OpenApiHeader:
|
|
|
90
90
|
elif private_key:
|
|
91
91
|
# Handle escaped newlines
|
|
92
92
|
private_key = private_key.replace("\\n", "\n")
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
|
|
94
|
+
# Check if private key already has PEM headers/footers
|
|
95
|
+
if not private_key.strip().startswith('-----BEGIN'):
|
|
96
|
+
# Raw base64 key content - add PEM headers and footers
|
|
97
|
+
private_key = f"-----BEGIN PRIVATE KEY-----\n{private_key}\n-----END PRIVATE KEY-----"
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
private_key_obj = serialization.load_pem_private_key(
|
|
101
|
+
private_key.encode(),
|
|
102
|
+
password=None,
|
|
103
|
+
backend=default_backend()
|
|
104
|
+
)
|
|
105
|
+
return private_key_obj
|
|
106
|
+
except ValueError as e:
|
|
107
|
+
# If loading fails, try alternative PEM formats
|
|
108
|
+
if "BEGIN PRIVATE KEY" in private_key:
|
|
109
|
+
# Already in PKCS#8 format, try RSA format
|
|
110
|
+
rsa_key = private_key.replace("-----BEGIN PRIVATE KEY-----", "-----BEGIN RSA PRIVATE KEY-----")
|
|
111
|
+
rsa_key = rsa_key.replace("-----END PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----")
|
|
112
|
+
try:
|
|
113
|
+
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
|
114
|
+
private_key_obj = load_pem_private_key(
|
|
115
|
+
rsa_key.encode(),
|
|
116
|
+
password=None,
|
|
117
|
+
backend=default_backend()
|
|
118
|
+
)
|
|
119
|
+
return private_key_obj
|
|
120
|
+
except:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
raise ValueError(f"Unable to load private key: {str(e)}. Please ensure the private key is in proper PEM format.")
|
|
99
124
|
else:
|
|
100
125
|
raise ValueError("Provide one of private_key or private_key_path")
|
|
101
126
|
|
|
@@ -155,14 +180,14 @@ class OpenApiHeader:
|
|
|
155
180
|
# Generate unique request message ID
|
|
156
181
|
req_msg_id = "sdk" + str(uuid.uuid4())[3:]
|
|
157
182
|
|
|
158
|
-
# Return auth settings following
|
|
183
|
+
# Return auth settings following correct field order: version, function, clientId, clientSecret, reqTime, reqMsgId, reserve
|
|
159
184
|
return {
|
|
160
185
|
VERSION: generateOpenApiSetting(key=VERSION, value=version),
|
|
161
186
|
FUNCTION: generateOpenApiSetting(key=FUNCTION, value=function_name),
|
|
162
187
|
CLIENT_ID: generateOpenApiSetting(key=CLIENT_ID, value=client_id),
|
|
188
|
+
CLIENT_SECRET: generateOpenApiSetting(key=CLIENT_SECRET, value=client_secret),
|
|
163
189
|
REQ_TIME: generateOpenApiSetting(key=REQ_TIME, value=timestamp),
|
|
164
190
|
REQ_MSG_ID: generateOpenApiSetting(key=REQ_MSG_ID, value=req_msg_id),
|
|
165
|
-
CLIENT_SECRET: generateOpenApiSetting(key=CLIENT_SECRET, value=client_secret),
|
|
166
191
|
RESERVE: generateOpenApiSetting(key=RESERVE, value="{}")
|
|
167
192
|
}
|
|
168
193
|
|
|
@@ -15,10 +15,23 @@
|
|
|
15
15
|
import importlib
|
|
16
16
|
import pkgutil
|
|
17
17
|
import os
|
|
18
|
+
import re
|
|
18
19
|
from typing import List
|
|
19
20
|
|
|
20
21
|
PACKAGE_NAME = 'dana'
|
|
21
22
|
|
|
23
|
+
# Only single path segments that are valid Python identifiers (no traversal / injection).
|
|
24
|
+
_SAFE_PKG_SEGMENT = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _assert_safe_import_segment(name: str, label: str) -> None:
|
|
28
|
+
if not name or not _SAFE_PKG_SEGMENT.fullmatch(name):
|
|
29
|
+
raise ValueError(
|
|
30
|
+
f"Refusing dynamic import: disallowed {label} {name!r} "
|
|
31
|
+
"(expected a single identifier-like package segment)"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
22
35
|
def import_all_models(base_path: str) -> None:
|
|
23
36
|
"""
|
|
24
37
|
Imports all model modules from subdirectories of the specified base path.
|
|
@@ -48,6 +61,8 @@ def import_all_models(base_path: str) -> None:
|
|
|
48
61
|
path_to_domain: List[str] = getattr(subdomain.module_finder, 'path', '')
|
|
49
62
|
domain = os.path.basename(path_to_domain)
|
|
50
63
|
|
|
51
|
-
# Construct the full module path and import
|
|
64
|
+
# Construct the full module path and import (whitelist segments — no dynamic/untrusted strings)
|
|
65
|
+
_assert_safe_import_segment(domain, "domain")
|
|
66
|
+
_assert_safe_import_segment(subdomain.name, "subdomain")
|
|
52
67
|
module_name = f"{PACKAGE_NAME}.{domain}.{subdomain.name}.models"
|
|
53
68
|
importlib.import_module(module_name)
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
|
22
22
|
Do not edit the class manually.
|
|
23
23
|
""" # noqa: E501
|
|
24
|
+
import json
|
|
24
25
|
import warnings
|
|
25
26
|
from pydantic import validate_call, Field, StrictFloat, StrictStr, StrictInt
|
|
26
27
|
from typing import Any, Dict, List, Optional, Tuple, Union, Type
|
|
@@ -1901,6 +1902,70 @@ class WidgetApi:
|
|
|
1901
1902
|
# authentication setting
|
|
1902
1903
|
_auth_settings: List[str] = [
|
|
1903
1904
|
]
|
|
1905
|
+
# For OPEN_API type, we use CLIENT_SECRET authentication with signature
|
|
1906
|
+
# merge list of auth settings with Open API runtime headers
|
|
1907
|
+
from dana.utils.open_api_header import OpenApiHeader
|
|
1908
|
+
_auth_settings = OpenApiHeader.merge_with_open_api_runtime_headers(_auth_settings)
|
|
1909
|
+
# Create the full request structure for OPEN_API
|
|
1910
|
+
# Wrap the simplified body parameters in the proper DANA request format
|
|
1911
|
+
# Extract accessToken from request body if available (widget-specific)
|
|
1912
|
+
request_body_dict = query_user_profile_request.to_dict()
|
|
1913
|
+
access_token = request_body_dict.get('accessToken', '')
|
|
1914
|
+
# Create body without accessToken (for queryUserProfile)
|
|
1915
|
+
body_without_access_token = {k: v for k, v in request_body_dict.items() if k != 'accessToken'}
|
|
1916
|
+
# Generate head parameters
|
|
1917
|
+
_generated_auth = OpenApiHeader.get_open_api_generated_headers(
|
|
1918
|
+
method='POST',
|
|
1919
|
+
resource_path='/dana/member/query/queryUserProfile.htm',
|
|
1920
|
+
body='{}', # Empty body for head generation
|
|
1921
|
+
function_name="dana.member.query.queryUserProfile",
|
|
1922
|
+
client_id=self.api_client.configuration.get_api_key_with_prefix('X_PARTNER_ID'),
|
|
1923
|
+
client_secret=self.api_client.configuration.get_api_key_with_prefix('CLIENT_SECRET')
|
|
1924
|
+
)
|
|
1925
|
+
# Extract values from auth settings for JSON body, ensuring correct field order
|
|
1926
|
+
head_params = {}
|
|
1927
|
+
# Add fields in the correct order: version, function, clientId, clientSecret, reqTime, reqMsgId
|
|
1928
|
+
for key in ['version', 'function', 'clientId', 'clientSecret', 'reqTime', 'reqMsgId']:
|
|
1929
|
+
if key in _generated_auth:
|
|
1930
|
+
head_params[key] = _generated_auth[key]['value']
|
|
1931
|
+
# Add accessToken before reserve if found in request body (widget-specific)
|
|
1932
|
+
if access_token:
|
|
1933
|
+
head_params['accessToken'] = access_token
|
|
1934
|
+
# Add reserve at the end
|
|
1935
|
+
if 'reserve' in _generated_auth:
|
|
1936
|
+
head_params['reserve'] = _generated_auth['reserve']['value']
|
|
1937
|
+
# Create the full request structure (use body without accessToken)
|
|
1938
|
+
request_body = {
|
|
1939
|
+
"request": {
|
|
1940
|
+
"head": head_params,
|
|
1941
|
+
"body": body_without_access_token
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
# Generate signature (pass full structure, function will extract request part)
|
|
1945
|
+
_signature = OpenApiHeader.generate_open_api_signature(
|
|
1946
|
+
body=json.dumps(request_body),
|
|
1947
|
+
private_key=self.api_client.configuration.get_api_key_with_prefix('PRIVATE_KEY')
|
|
1948
|
+
)
|
|
1949
|
+
# Add signature to the request
|
|
1950
|
+
if _signature:
|
|
1951
|
+
request_body['signature'] = _signature
|
|
1952
|
+
# Convert final request to JSON string
|
|
1953
|
+
_body_params = json.dumps(request_body)
|
|
1954
|
+
return self.api_client.param_serialize(
|
|
1955
|
+
method='POST',
|
|
1956
|
+
resource_path='/dana/member/query/queryUserProfile.htm',
|
|
1957
|
+
path_params=_path_params,
|
|
1958
|
+
query_params=_query_params,
|
|
1959
|
+
header_params=_header_params,
|
|
1960
|
+
body=_body_params,
|
|
1961
|
+
post_params=_form_params,
|
|
1962
|
+
files=_files,
|
|
1963
|
+
auth_settings=_auth_settings,
|
|
1964
|
+
collection_formats=_collection_formats,
|
|
1965
|
+
_host=_host,
|
|
1966
|
+
_request_auth=_request_auth,
|
|
1967
|
+
_generated_auth=_generated_auth,
|
|
1968
|
+
)
|
|
1904
1969
|
@validate_call
|
|
1905
1970
|
def refund_order(
|
|
1906
1971
|
self,
|