dana-python 2.2.1__tar.gz → 2.2.2__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 (251) hide show
  1. {dana_python-2.2.1/dana_python.egg-info → dana_python-2.2.2}/PKG-INFO +1 -1
  2. dana_python-2.2.2/dana/widget/v1/util.py +338 -0
  3. {dana_python-2.2.1 → dana_python-2.2.2/dana_python.egg-info}/PKG-INFO +1 -1
  4. {dana_python-2.2.1 → dana_python-2.2.2}/pyproject.toml +1 -1
  5. {dana_python-2.2.1 → dana_python-2.2.2}/tests/test_payment_gateway_api.py +14 -1
  6. {dana_python-2.2.1 → dana_python-2.2.2}/tests/test_payment_gateway_with_automation.py +13 -71
  7. {dana_python-2.2.1 → dana_python-2.2.2}/tests/test_widget_api.py +9 -3
  8. {dana_python-2.2.1 → dana_python-2.2.2}/tests/test_widget_with_automation.py +6 -5
  9. dana_python-2.2.1/dana/widget/v1/util.py +0 -289
  10. {dana_python-2.2.1 → dana_python-2.2.2}/LICENSE +0 -0
  11. {dana_python-2.2.1 → dana_python-2.2.2}/README.md +0 -0
  12. {dana_python-2.2.1 → dana_python-2.2.2}/dana/__init__.py +0 -0
  13. {dana_python-2.2.1 → dana_python-2.2.2}/dana/api_client.py +0 -0
  14. {dana_python-2.2.1 → dana_python-2.2.2}/dana/api_response.py +0 -0
  15. {dana_python-2.2.1 → dana_python-2.2.2}/dana/base/__init__.py +0 -0
  16. {dana_python-2.2.1 → dana_python-2.2.2}/dana/base/configuration.py +0 -0
  17. {dana_python-2.2.1 → dana_python-2.2.2}/dana/base/model.py +0 -0
  18. {dana_python-2.2.1 → dana_python-2.2.2}/dana/base/types.py +0 -0
  19. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/__init__.py +0 -0
  20. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/__init__.py +0 -0
  21. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/api/__init__.py +0 -0
  22. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/api/disbursement_api.py +0 -0
  23. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/custom_validation.py +0 -0
  24. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/enum.py +0 -0
  25. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/__init__.py +0 -0
  26. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/bank_account_inquiry_request.py +0 -0
  27. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/bank_account_inquiry_request_additional_info.py +0 -0
  28. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/bank_account_inquiry_response.py +0 -0
  29. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/bank_account_inquiry_response_additional_info.py +0 -0
  30. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/dana_account_inquiry_request.py +0 -0
  31. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/dana_account_inquiry_request_additional_info.py +0 -0
  32. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/dana_account_inquiry_response.py +0 -0
  33. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/money.py +0 -0
  34. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/transfer_to_bank_inquiry_status_request.py +0 -0
  35. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/transfer_to_bank_inquiry_status_response.py +0 -0
  36. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/transfer_to_bank_request.py +0 -0
  37. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/transfer_to_bank_request_additional_info.py +0 -0
  38. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/transfer_to_bank_response.py +0 -0
  39. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/transfer_to_dana_inquiry_status_request.py +0 -0
  40. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/transfer_to_dana_inquiry_status_response.py +0 -0
  41. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/transfer_to_dana_request.py +0 -0
  42. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/transfer_to_dana_request_additional_info.py +0 -0
  43. {dana_python-2.2.1 → dana_python-2.2.2}/dana/disbursement/v1/models/transfer_to_dana_response.py +0 -0
  44. {dana_python-2.2.1 → dana_python-2.2.2}/dana/exceptions.py +0 -0
  45. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/__init__.py +0 -0
  46. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/__init__.py +0 -0
  47. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/api/__init__.py +0 -0
  48. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/api/merchant_management_api.py +0 -0
  49. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/enum.py +0 -0
  50. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/__init__.py +0 -0
  51. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/address_info.py +0 -0
  52. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/asset_card_list_item.py +0 -0
  53. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/business_docs.py +0 -0
  54. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/create_division_request.py +0 -0
  55. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/create_division_request_ext_info.py +0 -0
  56. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/create_division_response.py +0 -0
  57. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/create_division_response_response.py +0 -0
  58. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/create_division_response_response_body.py +0 -0
  59. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/create_division_response_response_head.py +0 -0
  60. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/create_shop_request.py +0 -0
  61. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/create_shop_response.py +0 -0
  62. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/create_shop_response_response.py +0 -0
  63. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/create_shop_response_response_body.py +0 -0
  64. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/create_shop_response_response_head.py +0 -0
  65. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/division_resource_info.py +0 -0
  66. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/member_asset_result_info.py +0 -0
  67. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/merchant_account_info.py +0 -0
  68. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/merchant_certificate_info.py +0 -0
  69. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/merchant_contact_address.py +0 -0
  70. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/merchant_contact_email.py +0 -0
  71. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/merchant_contact_mobile_no.py +0 -0
  72. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/merchant_corporate_certificate.py +0 -0
  73. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/merchant_information.py +0 -0
  74. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/merchant_resource_information.py +0 -0
  75. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/mobile_no_info.py +0 -0
  76. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/pic_info.py +0 -0
  77. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_asset_card_list_request.py +0 -0
  78. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_asset_card_list_response.py +0 -0
  79. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_asset_card_list_response_response.py +0 -0
  80. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_asset_card_list_response_response_body.py +0 -0
  81. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_asset_card_list_response_response_head.py +0 -0
  82. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_division_request.py +0 -0
  83. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_division_response.py +0 -0
  84. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_division_response_response.py +0 -0
  85. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_division_response_response_body.py +0 -0
  86. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_division_response_response_head.py +0 -0
  87. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_merchant_info_request.py +0 -0
  88. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_merchant_info_response.py +0 -0
  89. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_merchant_info_response_response.py +0 -0
  90. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_merchant_info_response_response_body.py +0 -0
  91. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_merchant_info_response_response_head.py +0 -0
  92. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_merchant_resource_request.py +0 -0
  93. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_merchant_resource_response.py +0 -0
  94. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_merchant_resource_response_response.py +0 -0
  95. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_merchant_resource_response_response_body.py +0 -0
  96. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_merchant_resource_response_response_head.py +0 -0
  97. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_shop_request.py +0 -0
  98. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_shop_response.py +0 -0
  99. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_shop_response_response.py +0 -0
  100. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_shop_response_response_body.py +0 -0
  101. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/query_shop_response_response_head.py +0 -0
  102. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/result_info.py +0 -0
  103. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/shop_resource_info.py +0 -0
  104. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/update_division_request.py +0 -0
  105. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/update_division_response.py +0 -0
  106. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/update_division_response_response.py +0 -0
  107. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/update_division_response_response_body.py +0 -0
  108. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/update_division_response_response_head.py +0 -0
  109. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/update_shop_request.py +0 -0
  110. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/update_shop_response.py +0 -0
  111. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/update_shop_response_response.py +0 -0
  112. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/update_shop_response_response_head.py +0 -0
  113. {dana_python-2.2.1 → dana_python-2.2.2}/dana/merchant_management/v1/models/user_name.py +0 -0
  114. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/__init__.py +0 -0
  115. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/__init__.py +0 -0
  116. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/api/__init__.py +0 -0
  117. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/api/payment_gateway_api.py +0 -0
  118. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/custom_validation.py +0 -0
  119. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/enum.py +0 -0
  120. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/__init__.py +0 -0
  121. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/actor_context.py +0 -0
  122. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/amount_detail.py +0 -0
  123. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/audit_info.py +0 -0
  124. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/buyer.py +0 -0
  125. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/cancel_order_request.py +0 -0
  126. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/cancel_order_response.py +0 -0
  127. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/consult_pay_payment_info.py +0 -0
  128. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/consult_pay_request.py +0 -0
  129. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/consult_pay_request_additional_info.py +0 -0
  130. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/consult_pay_response.py +0 -0
  131. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/create_order_by_api_additional_info.py +0 -0
  132. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/create_order_by_api_request.py +0 -0
  133. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/create_order_by_redirect_additional_info.py +0 -0
  134. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/create_order_by_redirect_request.py +0 -0
  135. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/create_order_response.py +0 -0
  136. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/create_order_response_additional_info.py +0 -0
  137. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/env_info.py +0 -0
  138. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/goods.py +0 -0
  139. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/money.py +0 -0
  140. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/order_api_object.py +0 -0
  141. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/order_redirect_object.py +0 -0
  142. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/pay_option_additional_info.py +0 -0
  143. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/pay_option_detail.py +0 -0
  144. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/pay_option_info.py +0 -0
  145. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/payment_view.py +0 -0
  146. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/promo_info.py +0 -0
  147. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/query_payment_request.py +0 -0
  148. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/query_payment_response.py +0 -0
  149. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/query_payment_response_additional_info.py +0 -0
  150. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/refund_option_bill.py +0 -0
  151. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/refund_order_request.py +0 -0
  152. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/refund_order_request_additional_info.py +0 -0
  153. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/refund_order_response.py +0 -0
  154. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/seller.py +0 -0
  155. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/shipping_info.py +0 -0
  156. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/status_detail.py +0 -0
  157. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/time_detail.py +0 -0
  158. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/url_param.py +0 -0
  159. {dana_python-2.2.1 → dana_python-2.2.2}/dana/payment_gateway/v1/models/virtual_account_info.py +0 -0
  160. {dana_python-2.2.1 → dana_python-2.2.2}/dana/rest.py +0 -0
  161. {dana_python-2.2.1 → dana_python-2.2.2}/dana/utils/date_validation.py +0 -0
  162. {dana_python-2.2.1 → dana_python-2.2.2}/dana/utils/models.py +0 -0
  163. {dana_python-2.2.1 → dana_python-2.2.2}/dana/utils/open_api_configuration.py +0 -0
  164. {dana_python-2.2.1 → dana_python-2.2.2}/dana/utils/open_api_header.py +0 -0
  165. {dana_python-2.2.1 → dana_python-2.2.2}/dana/utils/script.py +0 -0
  166. {dana_python-2.2.1 → dana_python-2.2.2}/dana/utils/snap_configuration.py +0 -0
  167. {dana_python-2.2.1 → dana_python-2.2.2}/dana/utils/snap_header.py +0 -0
  168. {dana_python-2.2.1 → dana_python-2.2.2}/dana/utils/url.py +0 -0
  169. {dana_python-2.2.1 → dana_python-2.2.2}/dana/webhook/__init__.py +0 -0
  170. {dana_python-2.2.1 → dana_python-2.2.2}/dana/webhook/finish_notify_payment_info.py +0 -0
  171. {dana_python-2.2.1 → dana_python-2.2.2}/dana/webhook/finish_notify_request.py +0 -0
  172. {dana_python-2.2.1 → dana_python-2.2.2}/dana/webhook/finish_notify_request_additional_info.py +0 -0
  173. {dana_python-2.2.1 → dana_python-2.2.2}/dana/webhook/finish_notify_response.py +0 -0
  174. {dana_python-2.2.1 → dana_python-2.2.2}/dana/webhook/money.py +0 -0
  175. {dana_python-2.2.1 → dana_python-2.2.2}/dana/webhook/pay_option_info.py +0 -0
  176. {dana_python-2.2.1 → dana_python-2.2.2}/dana/webhook/shop_info.py +0 -0
  177. {dana_python-2.2.1 → dana_python-2.2.2}/dana/webhook/webhook.py +0 -0
  178. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/__init__.py +0 -0
  179. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/__init__.py +0 -0
  180. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/api/__init__.py +0 -0
  181. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/api/widget_api.py +0 -0
  182. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/custom_validation.py +0 -0
  183. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/enum.py +0 -0
  184. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/__init__.py +0 -0
  185. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/account_info.py +0 -0
  186. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/account_unbinding_request.py +0 -0
  187. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/account_unbinding_request_additional_info.py +0 -0
  188. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/account_unbinding_response.py +0 -0
  189. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/amount_detail.py +0 -0
  190. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/apply_ott_request.py +0 -0
  191. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/apply_ott_request_additional_info.py +0 -0
  192. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/apply_ott_response.py +0 -0
  193. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/apply_ott_response_user_resources_inner.py +0 -0
  194. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/apply_token_authorization_code_request.py +0 -0
  195. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/apply_token_refresh_token_request.py +0 -0
  196. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/apply_token_response.py +0 -0
  197. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/apply_token_response_additional_info.py +0 -0
  198. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/apply_token_response_additional_info_user_info.py +0 -0
  199. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/balance_inquiry_request.py +0 -0
  200. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/balance_inquiry_request_additional_info.py +0 -0
  201. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/balance_inquiry_response.py +0 -0
  202. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/buyer.py +0 -0
  203. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/cancel_order_request.py +0 -0
  204. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/cancel_order_response.py +0 -0
  205. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/env_info.py +0 -0
  206. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/goods.py +0 -0
  207. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/international_order_info.py +0 -0
  208. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/international_order_info_exchange_rate.py +0 -0
  209. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/money.py +0 -0
  210. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/oauth2_url_data.py +0 -0
  211. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/oauth2_url_data_seamless_data.py +0 -0
  212. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/order.py +0 -0
  213. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/pay_option_detail.py +0 -0
  214. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/pay_option_detail_additional_info.py +0 -0
  215. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/pay_option_info.py +0 -0
  216. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/payment_promo_info.py +0 -0
  217. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/payment_view.py +0 -0
  218. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/query_payment_request.py +0 -0
  219. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/query_payment_response.py +0 -0
  220. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/query_payment_response_additional_info.py +0 -0
  221. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/query_user_profile_request.py +0 -0
  222. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/query_user_profile_response.py +0 -0
  223. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/query_user_profile_response_response.py +0 -0
  224. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/query_user_profile_response_response_body.py +0 -0
  225. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/query_user_profile_response_response_head.py +0 -0
  226. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/refund_order_request.py +0 -0
  227. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/refund_order_request_additional_info.py +0 -0
  228. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/refund_order_response.py +0 -0
  229. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/refund_promo_info.py +0 -0
  230. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/result_info.py +0 -0
  231. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/seller.py +0 -0
  232. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/service_info.py +0 -0
  233. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/shipping_info.py +0 -0
  234. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/status_detail.py +0 -0
  235. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/time_detail.py +0 -0
  236. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/url_param.py +0 -0
  237. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/user_resource_info.py +0 -0
  238. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/virtual_account_info.py +0 -0
  239. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/widget_payment_request.py +0 -0
  240. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/widget_payment_request_additional_info.py +0 -0
  241. {dana_python-2.2.1 → dana_python-2.2.2}/dana/widget/v1/models/widget_payment_response.py +0 -0
  242. {dana_python-2.2.1 → dana_python-2.2.2}/dana_python.egg-info/SOURCES.txt +0 -0
  243. {dana_python-2.2.1 → dana_python-2.2.2}/dana_python.egg-info/dependency_links.txt +0 -0
  244. {dana_python-2.2.1 → dana_python-2.2.2}/dana_python.egg-info/requires.txt +0 -0
  245. {dana_python-2.2.1 → dana_python-2.2.2}/dana_python.egg-info/top_level.txt +0 -0
  246. {dana_python-2.2.1 → dana_python-2.2.2}/setup.cfg +0 -0
  247. {dana_python-2.2.1 → dana_python-2.2.2}/tests/test_disbursement_api.py +0 -0
  248. {dana_python-2.2.1 → dana_python-2.2.2}/tests/test_disbursement_custom_validation_enrich.py +0 -0
  249. {dana_python-2.2.1 → dana_python-2.2.2}/tests/test_merchant_management_api.py +0 -0
  250. {dana_python-2.2.1 → dana_python-2.2.2}/tests/test_snap_header.py +0 -0
  251. {dana_python-2.2.1 → dana_python-2.2.2}/tests/test_webhook.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: 2.2.2
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>
@@ -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: 2.2.2
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>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dana-python"
3
- version = "2.2.1"
3
+ version = "2.2.2"
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"
@@ -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:
@@ -13,32 +13,17 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import pytest
16
- import os
17
16
  import time
18
- import uuid
19
- from datetime import datetime
20
17
 
21
18
  from dana.payment_gateway.v1.api import PaymentGatewayApi
22
19
  from dana.payment_gateway.v1.models import (
23
- ConsultPayRequest,
24
- CreateOrderByApiRequest,
25
20
  CreateOrderByRedirectRequest,
26
21
  QueryPaymentRequest,
27
- CancelOrderRequest,
28
- RefundOrderRequest,
29
- Money
30
22
  )
31
23
 
32
24
  from tests.fixtures.api_client import api_instance_payment_gateway
33
- from tests.fixtures.payment_gateway import (
34
- consult_pay_request,
35
- create_order_by_api_request,
36
- create_order_by_api_paid_request,
37
- create_order_by_redirect_request,
38
- query_payment_request,
39
- cancel_order_request,
40
- refund_order_request
41
- )
25
+ from tests.helpers.env_credentials import merchant_id as get_merchant_id
26
+ from tests.fixtures.payment_gateway import create_order_by_redirect_request
42
27
  from tests.web_automation import automate_payment_payment_gateway
43
28
 
44
29
 
@@ -46,16 +31,12 @@ class TestPaymentGatewayWithAutomation:
46
31
  """Test class for Payment Gateway API endpoints with browser automation"""
47
32
 
48
33
  def test_complete_scenario(self, api_instance_payment_gateway: PaymentGatewayApi, create_order_by_redirect_request: CreateOrderByRedirectRequest):
49
- """Test RefundOrder operation with web automation
50
-
51
- This test does the following:
52
- 1. Creates an order
53
- 2. Automates payment via web redirect URL
54
- 3. Queries payment status until success (with retries)
55
- 4. Performs refund operation on the successful payment
34
+ """Create order, automate payment, and verify query payment returns SUCCESS.
35
+
36
+ Refund is skipped for now (sandbox returns 400 on refund in this flow).
56
37
  """
57
38
  # Skip test if no API client credentials are available
58
- merchant_id = os.getenv('MERCHANT_ID')
39
+ merchant_id = get_merchant_id()
59
40
  if not merchant_id:
60
41
  pytest.skip("Skipping test: No API client credentials")
61
42
 
@@ -69,9 +50,6 @@ class TestPaymentGatewayWithAutomation:
69
50
  assert resp_create_order.partner_reference_no == create_order_by_redirect_request.partner_reference_no
70
51
  assert resp_create_order.reference_no is not None
71
52
 
72
- # Store the reference number for refund
73
- ref_no = resp_create_order.reference_no
74
-
75
53
  # Flag to track if payment automation was successful
76
54
  payment_succeeded = False
77
55
 
@@ -91,7 +69,7 @@ class TestPaymentGatewayWithAutomation:
91
69
  else:
92
70
  print("Payment automation completed successfully!")
93
71
 
94
- # Only proceed with query payment and refund if payment was successful
72
+ # Only proceed with query payment if payment was successful
95
73
  if payment_succeeded:
96
74
  print("Payment automation successful. Proceeding with query payment...")
97
75
 
@@ -102,8 +80,9 @@ class TestPaymentGatewayWithAutomation:
102
80
  merchant_id=merchant_id
103
81
  )
104
82
 
105
- # Try query payment up to 3 times until we get 'SUCCESS' status
106
- max_retries = 3
83
+ # Try query payment until SUCCESS (sandbox can lag after pay)
84
+ max_retries = 6
85
+ retry_delay_seconds = 5
107
86
  query_succeeded = False
108
87
  resp_query_payment = None
109
88
 
@@ -125,8 +104,8 @@ class TestPaymentGatewayWithAutomation:
125
104
  print(f"Query payment attempt {i+1} failed: {e}")
126
105
 
127
106
  if i < max_retries - 1:
128
- print("Waiting 5 seconds before retrying query...")
129
- time.sleep(5)
107
+ print(f"Waiting {retry_delay_seconds} seconds before retrying query...")
108
+ time.sleep(retry_delay_seconds)
130
109
 
131
110
  # Log the query results
132
111
  if query_succeeded:
@@ -140,44 +119,7 @@ class TestPaymentGatewayWithAutomation:
140
119
  print(f"Payment details: Amount={resp_query_payment.amount.value} "
141
120
  f"{resp_query_payment.amount.currency}")
142
121
  else:
143
- pytest.fail("All query payment attempts failed or did not return SUCCESS status. Continuing with refund anyway.")
144
-
145
- print("Proceeding with refund order request...")
146
-
147
- # Create refund order request
148
- refund_request = RefundOrderRequest(
149
- original_partner_reference_no=create_order_by_redirect_request.partner_reference_no,
150
- merchant_id=merchant_id,
151
- partner_refund_no=str(uuid.uuid4()),
152
- refund_amount=Money(
153
- value=create_order_by_redirect_request.amount.value,
154
- currency=create_order_by_redirect_request.amount.currency
155
- ),
156
- reason="Python SDK Automation Test Refund"
157
- )
158
-
159
- # Execute the refund order API call
160
- try:
161
- resp_refund_order = api_instance_payment_gateway.refund_order(refund_request)
162
-
163
- # Add assertions for successful refund response
164
- assert resp_refund_order is not None
165
-
166
- assert resp_refund_order.response_code is not None
167
-
168
- if resp_refund_order.response_code:
169
- assert resp_refund_order.response_code in ['2005800', '2001400'], "Unexpected response code"
170
-
171
- print(f"Refund response: {resp_refund_order}")
172
- except Exception as e:
173
- error_str = str(e)
174
- print(f"Refund order request error: {error_str}")
175
-
176
- # If we get a 404 with "Invalid Transaction Status", that's expected in test environment
177
- if "Invalid Transaction Status" in error_str:
178
- print("Received expected 'Invalid Transaction Status' error")
179
- else:
180
- pytest.fail(f"Refund request failed with unexpected error: {e}")
122
+ pytest.fail("All query payment attempts failed or did not return SUCCESS status")
181
123
  else:
182
124
  pytest.fail("Payment automation was not successful")
183
125
  except Exception as e:
@@ -12,9 +12,10 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- import pytest
16
15
  import os
17
16
 
17
+ import pytest
18
+
18
19
  from dana.widget.v1.api import WidgetApi
19
20
  from dana.rest import ApiException
20
21
  from dana.widget.v1.models import (
@@ -26,6 +27,7 @@ from dana.widget.v1.models import (
26
27
 
27
28
  from tests.fixtures.api_client import api_instance_widget
28
29
  from tests.fixtures.widget import widget_payment_request, apply_token_request, widget_cancel_order_request, widget_payment_request_with_valid_up_to_beyond_30_minutes
30
+ from tests.helpers.env_credentials import merchant_id as get_merchant_id
29
31
 
30
32
  class TestWidgetApi:
31
33
  """Test class for Widget API endpoints without automation"""
@@ -47,7 +49,7 @@ class TestWidgetApi:
47
49
  print("Step 2: Querying payment status...")
48
50
  query_payment_request = QueryPaymentRequest(
49
51
  service_code="54",
50
- merchant_id=os.environ.get('MERCHANT_ID', '216620010016033632482'),
52
+ merchant_id=get_merchant_id("WIDGET"),
51
53
  original_partner_reference_no=widget_payment_request.partner_reference_no,
52
54
  original_reference_no=widget_payment_response.reference_no if hasattr(widget_payment_response, 'reference_no') else None
53
55
  )
@@ -61,6 +63,10 @@ class TestWidgetApi:
61
63
  except Exception as e:
62
64
  pytest.fail(f"Error during Widget Payment or Query execution: {e}")
63
65
 
66
+ @pytest.mark.skipif(
67
+ "CI" in os.environ or "GITLAB_CI" in os.environ,
68
+ reason="CI: sandbox merchant lacks cancel permission (4035715)",
69
+ )
64
70
  def test_widget_cancel_order(self, api_instance_widget: WidgetApi, widget_payment_request):
65
71
  """Test Widget Cancel Order flow
66
72
 
@@ -74,7 +80,7 @@ class TestWidgetApi:
74
80
  assert widget_payment_response is not None
75
81
 
76
82
  # Create cancel request directly
77
- merchant_id = os.environ.get('DANA_MERCHANT_ID', '216620000000147517713')
83
+ merchant_id = get_merchant_id("WIDGET")
78
84
  if hasattr(widget_payment_request, 'merchant_id') and widget_payment_request.merchant_id is not None:
79
85
  merchant_id = widget_payment_request.merchant_id
80
86
 
@@ -44,6 +44,7 @@ from tests.fixtures.widget import (
44
44
  account_unbinding_request
45
45
  )
46
46
  from tests.automate_oauth import get_auth_code
47
+ from tests.helpers.env_credentials import merchant_id as get_merchant_id, partner_id, private_key, redirect_url_oauth
47
48
 
48
49
 
49
50
  class TestWidgetWithAutomation:
@@ -57,12 +58,12 @@ class TestWidgetWithAutomation:
57
58
 
58
59
  This test runs the complete binding flow from OAuth to ApplyToken to ApplyOTT
59
60
  """
60
- merchant_id = os.getenv('MERCHANT_ID')
61
- private_key = os.getenv('PRIVATE_KEY')
61
+ merchant_id = get_merchant_id("WIDGET")
62
+ widget_private_key = private_key("WIDGET")
62
63
 
63
64
  # Step 1: Generate OAuth URL
64
65
  oauth2_url_data = Oauth2UrlData(
65
- redirect_url='https://google.com',
66
+ redirect_url=redirect_url_oauth(),
66
67
  merchant_id=merchant_id,
67
68
  seamless_data={
68
69
  'mobileNumber': '083811223355'
@@ -72,7 +73,7 @@ class TestWidgetWithAutomation:
72
73
 
73
74
  oauth_url = Util.generate_oauth_url(
74
75
  data=oauth2_url_data,
75
- private_key=private_key
76
+ private_key=widget_private_key
76
77
  )
77
78
 
78
79
  print(f'Generated OAuth URL for binding flow: {oauth_url}')
@@ -83,7 +84,7 @@ class TestWidgetWithAutomation:
83
84
  url_parts = urlparse(oauth_url)
84
85
  url_params = parse_qs(url_parts.query)
85
86
 
86
- assert url_params.get('partnerId', [''])[0] == os.getenv('X_PARTNER_ID'), 'Partner ID should match'
87
+ assert url_params.get('partnerId', [''])[0] == partner_id("WIDGET"), 'Partner ID should match'
87
88
  assert url_params.get('merchantId', [''])[0] == merchant_id, 'Merchant ID should match'
88
89
  assert 'channelId' in url_params, 'Should have channelId'
89
90
  assert 'timestamp' in url_params, 'Should have timestamp'