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.
Files changed (255) hide show
  1. {dana_python-2.2.1/dana_python.egg-info → dana_python-3.0.0}/PKG-INFO +7 -10
  2. {dana_python-2.2.1 → dana_python-3.0.0}/README.md +1 -1
  3. {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/snap_configuration.py +3 -3
  4. {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/webhook.py +38 -16
  5. dana_python-3.0.0/dana/widget/v1/util.py +338 -0
  6. {dana_python-2.2.1 → dana_python-3.0.0/dana_python.egg-info}/PKG-INFO +7 -10
  7. {dana_python-2.2.1 → dana_python-3.0.0}/dana_python.egg-info/SOURCES.txt +3 -0
  8. dana_python-3.0.0/dana_python.egg-info/requires.txt +7 -0
  9. {dana_python-2.2.1 → dana_python-3.0.0}/pyproject.toml +6 -9
  10. dana_python-3.0.0/tests/test_dependency_floors.py +25 -0
  11. {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_payment_gateway_api.py +14 -1
  12. {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_payment_gateway_with_automation.py +13 -71
  13. dana_python-3.0.0/tests/test_snap_configuration.py +15 -0
  14. {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_webhook.py +66 -0
  15. dana_python-3.0.0/tests/test_webhook_key_selection.py +35 -0
  16. {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_widget_api.py +9 -3
  17. {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_widget_with_automation.py +6 -5
  18. dana_python-2.2.1/dana/widget/v1/util.py +0 -289
  19. dana_python-2.2.1/dana_python.egg-info/requires.txt +0 -10
  20. {dana_python-2.2.1 → dana_python-3.0.0}/LICENSE +0 -0
  21. {dana_python-2.2.1 → dana_python-3.0.0}/dana/__init__.py +0 -0
  22. {dana_python-2.2.1 → dana_python-3.0.0}/dana/api_client.py +0 -0
  23. {dana_python-2.2.1 → dana_python-3.0.0}/dana/api_response.py +0 -0
  24. {dana_python-2.2.1 → dana_python-3.0.0}/dana/base/__init__.py +0 -0
  25. {dana_python-2.2.1 → dana_python-3.0.0}/dana/base/configuration.py +0 -0
  26. {dana_python-2.2.1 → dana_python-3.0.0}/dana/base/model.py +0 -0
  27. {dana_python-2.2.1 → dana_python-3.0.0}/dana/base/types.py +0 -0
  28. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/__init__.py +0 -0
  29. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/__init__.py +0 -0
  30. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/api/__init__.py +0 -0
  31. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/api/disbursement_api.py +0 -0
  32. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/custom_validation.py +0 -0
  33. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/enum.py +0 -0
  34. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/__init__.py +0 -0
  35. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/bank_account_inquiry_request.py +0 -0
  36. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/bank_account_inquiry_request_additional_info.py +0 -0
  37. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/bank_account_inquiry_response.py +0 -0
  38. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/bank_account_inquiry_response_additional_info.py +0 -0
  39. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/dana_account_inquiry_request.py +0 -0
  40. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/dana_account_inquiry_request_additional_info.py +0 -0
  41. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/dana_account_inquiry_response.py +0 -0
  42. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/money.py +0 -0
  43. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_bank_inquiry_status_request.py +0 -0
  44. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_bank_inquiry_status_response.py +0 -0
  45. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_bank_request.py +0 -0
  46. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_bank_request_additional_info.py +0 -0
  47. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_bank_response.py +0 -0
  48. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_dana_inquiry_status_request.py +0 -0
  49. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_dana_inquiry_status_response.py +0 -0
  50. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_dana_request.py +0 -0
  51. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_dana_request_additional_info.py +0 -0
  52. {dana_python-2.2.1 → dana_python-3.0.0}/dana/disbursement/v1/models/transfer_to_dana_response.py +0 -0
  53. {dana_python-2.2.1 → dana_python-3.0.0}/dana/exceptions.py +0 -0
  54. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/__init__.py +0 -0
  55. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/__init__.py +0 -0
  56. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/api/__init__.py +0 -0
  57. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/api/merchant_management_api.py +0 -0
  58. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/enum.py +0 -0
  59. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/__init__.py +0 -0
  60. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/address_info.py +0 -0
  61. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/asset_card_list_item.py +0 -0
  62. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/business_docs.py +0 -0
  63. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_division_request.py +0 -0
  64. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_division_request_ext_info.py +0 -0
  65. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_division_response.py +0 -0
  66. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_division_response_response.py +0 -0
  67. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_division_response_response_body.py +0 -0
  68. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_division_response_response_head.py +0 -0
  69. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_shop_request.py +0 -0
  70. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_shop_response.py +0 -0
  71. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_shop_response_response.py +0 -0
  72. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_shop_response_response_body.py +0 -0
  73. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/create_shop_response_response_head.py +0 -0
  74. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/division_resource_info.py +0 -0
  75. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/member_asset_result_info.py +0 -0
  76. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_account_info.py +0 -0
  77. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_certificate_info.py +0 -0
  78. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_contact_address.py +0 -0
  79. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_contact_email.py +0 -0
  80. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_contact_mobile_no.py +0 -0
  81. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_corporate_certificate.py +0 -0
  82. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_information.py +0 -0
  83. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/merchant_resource_information.py +0 -0
  84. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/mobile_no_info.py +0 -0
  85. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/pic_info.py +0 -0
  86. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_asset_card_list_request.py +0 -0
  87. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_asset_card_list_response.py +0 -0
  88. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_asset_card_list_response_response.py +0 -0
  89. {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
  90. {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
  91. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_division_request.py +0 -0
  92. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_division_response.py +0 -0
  93. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_division_response_response.py +0 -0
  94. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_division_response_response_body.py +0 -0
  95. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_division_response_response_head.py +0 -0
  96. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_info_request.py +0 -0
  97. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_info_response.py +0 -0
  98. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_info_response_response.py +0 -0
  99. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_info_response_response_body.py +0 -0
  100. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_info_response_response_head.py +0 -0
  101. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_resource_request.py +0 -0
  102. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_resource_response.py +0 -0
  103. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_resource_response_response.py +0 -0
  104. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_resource_response_response_body.py +0 -0
  105. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_merchant_resource_response_response_head.py +0 -0
  106. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_shop_request.py +0 -0
  107. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_shop_response.py +0 -0
  108. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_shop_response_response.py +0 -0
  109. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_shop_response_response_body.py +0 -0
  110. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/query_shop_response_response_head.py +0 -0
  111. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/result_info.py +0 -0
  112. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/shop_resource_info.py +0 -0
  113. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_division_request.py +0 -0
  114. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_division_response.py +0 -0
  115. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_division_response_response.py +0 -0
  116. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_division_response_response_body.py +0 -0
  117. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_division_response_response_head.py +0 -0
  118. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_shop_request.py +0 -0
  119. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_shop_response.py +0 -0
  120. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_shop_response_response.py +0 -0
  121. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/update_shop_response_response_head.py +0 -0
  122. {dana_python-2.2.1 → dana_python-3.0.0}/dana/merchant_management/v1/models/user_name.py +0 -0
  123. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/__init__.py +0 -0
  124. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/__init__.py +0 -0
  125. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/api/__init__.py +0 -0
  126. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/api/payment_gateway_api.py +0 -0
  127. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/custom_validation.py +0 -0
  128. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/enum.py +0 -0
  129. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/__init__.py +0 -0
  130. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/actor_context.py +0 -0
  131. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/amount_detail.py +0 -0
  132. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/audit_info.py +0 -0
  133. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/buyer.py +0 -0
  134. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/cancel_order_request.py +0 -0
  135. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/cancel_order_response.py +0 -0
  136. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/consult_pay_payment_info.py +0 -0
  137. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/consult_pay_request.py +0 -0
  138. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/consult_pay_request_additional_info.py +0 -0
  139. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/consult_pay_response.py +0 -0
  140. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/create_order_by_api_additional_info.py +0 -0
  141. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/create_order_by_api_request.py +0 -0
  142. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/create_order_by_redirect_additional_info.py +0 -0
  143. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/create_order_by_redirect_request.py +0 -0
  144. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/create_order_response.py +0 -0
  145. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/create_order_response_additional_info.py +0 -0
  146. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/env_info.py +0 -0
  147. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/goods.py +0 -0
  148. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/money.py +0 -0
  149. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/order_api_object.py +0 -0
  150. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/order_redirect_object.py +0 -0
  151. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/pay_option_additional_info.py +0 -0
  152. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/pay_option_detail.py +0 -0
  153. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/pay_option_info.py +0 -0
  154. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/payment_view.py +0 -0
  155. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/promo_info.py +0 -0
  156. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/query_payment_request.py +0 -0
  157. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/query_payment_response.py +0 -0
  158. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/query_payment_response_additional_info.py +0 -0
  159. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/refund_option_bill.py +0 -0
  160. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/refund_order_request.py +0 -0
  161. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/refund_order_request_additional_info.py +0 -0
  162. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/refund_order_response.py +0 -0
  163. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/seller.py +0 -0
  164. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/shipping_info.py +0 -0
  165. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/status_detail.py +0 -0
  166. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/time_detail.py +0 -0
  167. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/url_param.py +0 -0
  168. {dana_python-2.2.1 → dana_python-3.0.0}/dana/payment_gateway/v1/models/virtual_account_info.py +0 -0
  169. {dana_python-2.2.1 → dana_python-3.0.0}/dana/rest.py +0 -0
  170. {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/date_validation.py +0 -0
  171. {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/models.py +0 -0
  172. {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/open_api_configuration.py +0 -0
  173. {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/open_api_header.py +0 -0
  174. {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/script.py +0 -0
  175. {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/snap_header.py +0 -0
  176. {dana_python-2.2.1 → dana_python-3.0.0}/dana/utils/url.py +0 -0
  177. {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/__init__.py +0 -0
  178. {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/finish_notify_payment_info.py +0 -0
  179. {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/finish_notify_request.py +0 -0
  180. {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/finish_notify_request_additional_info.py +0 -0
  181. {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/finish_notify_response.py +0 -0
  182. {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/money.py +0 -0
  183. {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/pay_option_info.py +0 -0
  184. {dana_python-2.2.1 → dana_python-3.0.0}/dana/webhook/shop_info.py +0 -0
  185. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/__init__.py +0 -0
  186. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/__init__.py +0 -0
  187. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/api/__init__.py +0 -0
  188. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/api/widget_api.py +0 -0
  189. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/custom_validation.py +0 -0
  190. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/enum.py +0 -0
  191. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/__init__.py +0 -0
  192. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/account_info.py +0 -0
  193. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/account_unbinding_request.py +0 -0
  194. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/account_unbinding_request_additional_info.py +0 -0
  195. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/account_unbinding_response.py +0 -0
  196. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/amount_detail.py +0 -0
  197. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_ott_request.py +0 -0
  198. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_ott_request_additional_info.py +0 -0
  199. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_ott_response.py +0 -0
  200. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_ott_response_user_resources_inner.py +0 -0
  201. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_token_authorization_code_request.py +0 -0
  202. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_token_refresh_token_request.py +0 -0
  203. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_token_response.py +0 -0
  204. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_token_response_additional_info.py +0 -0
  205. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/apply_token_response_additional_info_user_info.py +0 -0
  206. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/balance_inquiry_request.py +0 -0
  207. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/balance_inquiry_request_additional_info.py +0 -0
  208. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/balance_inquiry_response.py +0 -0
  209. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/buyer.py +0 -0
  210. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/cancel_order_request.py +0 -0
  211. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/cancel_order_response.py +0 -0
  212. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/env_info.py +0 -0
  213. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/goods.py +0 -0
  214. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/international_order_info.py +0 -0
  215. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/international_order_info_exchange_rate.py +0 -0
  216. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/money.py +0 -0
  217. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/oauth2_url_data.py +0 -0
  218. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/oauth2_url_data_seamless_data.py +0 -0
  219. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/order.py +0 -0
  220. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/pay_option_detail.py +0 -0
  221. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/pay_option_detail_additional_info.py +0 -0
  222. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/pay_option_info.py +0 -0
  223. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/payment_promo_info.py +0 -0
  224. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/payment_view.py +0 -0
  225. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_payment_request.py +0 -0
  226. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_payment_response.py +0 -0
  227. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_payment_response_additional_info.py +0 -0
  228. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_user_profile_request.py +0 -0
  229. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_user_profile_response.py +0 -0
  230. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_user_profile_response_response.py +0 -0
  231. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_user_profile_response_response_body.py +0 -0
  232. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/query_user_profile_response_response_head.py +0 -0
  233. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/refund_order_request.py +0 -0
  234. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/refund_order_request_additional_info.py +0 -0
  235. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/refund_order_response.py +0 -0
  236. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/refund_promo_info.py +0 -0
  237. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/result_info.py +0 -0
  238. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/seller.py +0 -0
  239. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/service_info.py +0 -0
  240. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/shipping_info.py +0 -0
  241. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/status_detail.py +0 -0
  242. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/time_detail.py +0 -0
  243. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/url_param.py +0 -0
  244. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/user_resource_info.py +0 -0
  245. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/virtual_account_info.py +0 -0
  246. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/widget_payment_request.py +0 -0
  247. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/widget_payment_request_additional_info.py +0 -0
  248. {dana_python-2.2.1 → dana_python-3.0.0}/dana/widget/v1/models/widget_payment_response.py +0 -0
  249. {dana_python-2.2.1 → dana_python-3.0.0}/dana_python.egg-info/dependency_links.txt +0 -0
  250. {dana_python-2.2.1 → dana_python-3.0.0}/dana_python.egg-info/top_level.txt +0 -0
  251. {dana_python-2.2.1 → dana_python-3.0.0}/setup.cfg +0 -0
  252. {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_disbursement_api.py +0 -0
  253. {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_disbursement_custom_validation_enrich.py +0 -0
  254. {dana_python-2.2.1 → dana_python-3.0.0}/tests/test_merchant_management_api.py +0 -0
  255. {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: 2.2.1
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: >3.9.1
11
+ Requires-Python: >=3.10
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
- Requires-Dist: annotated-types==0.7.0
15
- Requires-Dist: cffi==2.0.0
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==2.9.0.post0
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.3.0
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
@@ -37,7 +37,7 @@ Here is the link: https://github.com/dana-id/uat-script.
37
37
 
38
38
  ## Requirements.
39
39
 
40
- Python 3.9.1+
40
+ Python 3.10+ (patched releases: 3.11.17, 3.12.15, 3.13.17, 3.14.9, 3.15.1 or later)
41
41
 
42
42
  ## Installation & Usage
43
43
  ### pip install
@@ -231,9 +231,9 @@ class SnapConfiguration(BaseConfiguration):
231
231
  ) -> None:
232
232
  """Constructor
233
233
  """
234
- self.host = "http://api.sandbox.dana.id" if (api_key["DANA_ENV"] == Env.SANDBOX or api_key["ENV"] == Env.SANDBOX) else \
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 = "http://api.sandbox.dana.id" if (api_key["DANA_ENV"] == Env.SANDBOX or api_key["ENV"] == Env.SANDBOX) else \
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': "http://api.sandbox.dana.id",
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 neither public_key nor public_key_path is provided,
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: 2.2.1
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: >3.9.1
11
+ Requires-Python: >=3.10
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
- Requires-Dist: annotated-types==0.7.0
15
- Requires-Dist: cffi==2.0.0
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==2.9.0.post0
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.3.0
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
@@ -0,0 +1,7 @@
1
+ annotated-types>=0.7.0
2
+ cryptography>=50.0.0
3
+ pydantic<3.0.0,>=2.10.6
4
+ pydantic-core<3.0.0,>=2.27.2
5
+ python-dateutil>=2.9.0.post0
6
+ typing-extensions<5.0.0,>=4.12.2
7
+ urllib3<3.0.0,>=2.8.0
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "dana-python"
3
- version = "2.2.1"
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 = ">3.9.1"
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==0.7.0",
17
- "cffi==2.0.0",
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==2.9.0.post0",
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.3.0,<3.0.0"
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 = 'test_shop'
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: