velocity-python 0.1.26__tar.gz → 0.1.28__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 (188) hide show
  1. {velocity_python-0.1.26/src/velocity_python.egg-info → velocity_python-0.1.28}/PKG-INFO +1 -1
  2. {velocity_python-0.1.26 → velocity_python-0.1.28}/pyproject.toml +1 -1
  3. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/assets/indexing.py +12 -2
  5. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/payment/authorizenet_adapter.py +185 -14
  6. velocity_python-0.1.28/src/velocity/payment/authorizenet_mirror.py +211 -0
  7. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/payment/braintree_adapter.py +74 -28
  8. velocity_python-0.1.28/src/velocity/payment/braintree_mirror.py +210 -0
  9. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/payment/stripe_adapter.py +129 -40
  10. velocity_python-0.1.28/src/velocity/payment/stripe_mirror.py +310 -0
  11. {velocity_python-0.1.26 → velocity_python-0.1.28/src/velocity_python.egg-info}/PKG-INFO +1 -1
  12. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity_python.egg-info/SOURCES.txt +5 -0
  13. velocity_python-0.1.28/tests/test_asset_indexing.py +103 -0
  14. velocity_python-0.1.28/tests/test_payment_authorizenet_adapter.py +173 -0
  15. velocity_python-0.1.28/tests/test_payment_braintree_adapter.py +182 -0
  16. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_payment_stripe_adapter.py +175 -1
  17. velocity_python-0.1.26/tests/test_payment_braintree_adapter.py +0 -77
  18. {velocity_python-0.1.26 → velocity_python-0.1.28}/LICENSE +0 -0
  19. {velocity_python-0.1.26 → velocity_python-0.1.28}/README.md +0 -0
  20. {velocity_python-0.1.26 → velocity_python-0.1.28}/setup.cfg +0 -0
  21. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/__init__.py +0 -0
  22. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/amplify.py +0 -0
  23. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/amplify_build.py +0 -0
  24. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/assets/__init__.py +0 -0
  25. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/assets/backfill.py +0 -0
  26. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/assets/references.py +0 -0
  27. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/assets/service.py +0 -0
  28. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/assets/usage_index.py +0 -0
  29. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/dirty_pipeline.py +0 -0
  30. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/handlers/__init__.py +0 -0
  31. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/handlers/base_handler.py +0 -0
  32. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/handlers/context.py +0 -0
  33. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/handlers/context_factory.py +0 -0
  34. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/handlers/exceptions.py +0 -0
  35. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  36. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  37. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
  38. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
  39. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/handlers/perf.py +0 -0
  40. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/handlers/response.py +0 -0
  41. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  42. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/s3.py +0 -0
  43. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/ssm_config.py +0 -0
  44. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/tests/__init__.py +0 -0
  45. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
  46. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  47. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/aws/tests/test_response.py +0 -0
  48. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/__init__.py +0 -0
  49. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/core/__init__.py +0 -0
  50. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/core/async_support.py +0 -0
  51. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/core/column.py +0 -0
  52. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/core/database.py +0 -0
  53. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/core/decorators.py +0 -0
  54. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/core/engine.py +0 -0
  55. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/core/result.py +0 -0
  56. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/core/row.py +0 -0
  57. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/core/sequence.py +0 -0
  58. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/core/table.py +0 -0
  59. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/core/transaction.py +0 -0
  60. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/core/view.py +0 -0
  61. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/exceptions.py +0 -0
  62. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/migrations.py +0 -0
  63. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/__init__.py +0 -0
  64. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/base/__init__.py +0 -0
  65. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/base/initializer.py +0 -0
  66. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/base/operators.py +0 -0
  67. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/base/sql.py +0 -0
  68. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/base/types.py +0 -0
  69. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/mysql/__init__.py +0 -0
  70. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/mysql/operators.py +0 -0
  71. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/mysql/reserved.py +0 -0
  72. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/mysql/sql.py +0 -0
  73. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/mysql/types.py +0 -0
  74. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/postgres/__init__.py +0 -0
  75. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/postgres/operators.py +0 -0
  76. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/postgres/reserved.py +0 -0
  77. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/postgres/sql.py +0 -0
  78. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/postgres/types.py +0 -0
  79. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  80. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/sqlite/operators.py +0 -0
  81. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  82. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/sqlite/sql.py +0 -0
  83. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/sqlite/types.py +0 -0
  84. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  85. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  86. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  87. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  88. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/sqlserver/types.py +0 -0
  89. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/servers/tablehelper.py +0 -0
  90. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/__init__.py +0 -0
  91. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/common_db_test.py +0 -0
  92. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/__init__.py +0 -0
  93. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/common.py +0 -0
  94. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_column.py +0 -0
  95. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  96. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_database.py +0 -0
  97. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  98. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  99. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  100. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_result.py +0 -0
  101. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_row.py +0 -0
  102. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  103. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  104. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  105. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  106. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  107. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_table.py +0 -0
  108. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  109. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  110. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/sql/__init__.py +0 -0
  111. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/sql/common.py +0 -0
  112. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  113. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  114. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  115. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/test_db_utils.py +0 -0
  116. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/test_postgres.py +0 -0
  117. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  118. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  119. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/test_result_caching.py +0 -0
  120. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  121. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  122. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  123. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  124. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/test_sql_builder.py +0 -0
  125. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/test_tablehelper.py +0 -0
  126. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/tests/test_view_helper.py +0 -0
  127. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/db/utils.py +0 -0
  128. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/logging.py +0 -0
  129. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/__init__.py +0 -0
  130. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/conv/__init__.py +0 -0
  131. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/conv/iconv.py +0 -0
  132. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/conv/oconv.py +0 -0
  133. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/db.py +0 -0
  134. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/export.py +0 -0
  135. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/format.py +0 -0
  136. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/mail.py +0 -0
  137. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/merge.py +0 -0
  138. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/pdf.py +0 -0
  139. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/tests/__init__.py +0 -0
  140. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/tests/test_db.py +0 -0
  141. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/tests/test_fix.py +0 -0
  142. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/tests/test_format.py +0 -0
  143. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/tests/test_iconv.py +0 -0
  144. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/tests/test_merge.py +0 -0
  145. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/tests/test_oconv.py +0 -0
  146. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/tests/test_original_error.py +0 -0
  147. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/tests/test_timer.py +0 -0
  148. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/timer.py +0 -0
  149. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/misc/tools.py +0 -0
  150. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/payment/__init__.py +0 -0
  151. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/payment/base_adapter.py +0 -0
  152. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity/payment/charge_rules.py +0 -0
  153. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  154. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity_python.egg-info/entry_points.txt +0 -0
  155. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity_python.egg-info/requires.txt +0 -0
  156. {velocity_python-0.1.26 → velocity_python-0.1.28}/src/velocity_python.egg-info/top_level.txt +0 -0
  157. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_amplify_build.py +0 -0
  158. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_asset_references.py +0 -0
  159. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_assets_service.py +0 -0
  160. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_async_support.py +0 -0
  161. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_batch_operations.py +0 -0
  162. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_concurrency_safety.py +0 -0
  163. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_connection_pool.py +0 -0
  164. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_connection_resilience.py +0 -0
  165. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_decorators.py +0 -0
  166. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_dirty_pipeline_fast_path.py +0 -0
  167. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_email_processing.py +0 -0
  168. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_iconv_money_to_cents.py +0 -0
  169. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_lambda_handler.py +0 -0
  170. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_lambda_handler_auth.py +0 -0
  171. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_mixins_import.py +0 -0
  172. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_n_plus_one.py +0 -0
  173. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_observability.py +0 -0
  174. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_payment_profile_sorting.py +0 -0
  175. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_pdf.py +0 -0
  176. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_prepared_statements.py +0 -0
  177. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_psycopg3_upgrade.py +0 -0
  178. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_query_cache.py +0 -0
  179. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_row_batch_update.py +0 -0
  180. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_row_cache_staleness.py +0 -0
  181. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_row_dirty_tracking.py +0 -0
  182. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_schema_migrations.py +0 -0
  183. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_security_hardening.py +0 -0
  184. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_spreadsheet_functions.py +0 -0
  185. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_sqs_per_record_transactions.py +0 -0
  186. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  187. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_table_alter.py +0 -0
  188. {velocity_python-0.1.26 → velocity_python-0.1.28}/tests/test_where_clause_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.26
3
+ Version: 0.1.28
4
4
  Summary: A rapid application development library for interfacing with data storage
5
5
  Author-email: Velocity Team <info@codeclubs.org>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "velocity-python"
7
- version = "0.1.26"
7
+ version = "0.1.28"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.1.26"
1
+ __version__ = version = "0.1.28"
2
2
 
3
3
  import importlib as _importlib
4
4
 
@@ -76,7 +76,11 @@ def reconcile_form_builder_template_asset_usages(
76
76
 
77
77
 
78
78
  def load_asset_usages(tx, *, source_table: str, source_sys_id: str) -> list[AssetUsage]:
79
- rows = tx.table("asset_usage_index").select(
79
+ usage_index = tx.table("asset_usage_index")
80
+ if not usage_index.exists():
81
+ return []
82
+
83
+ rows = usage_index.select(
80
84
  where={
81
85
  "source_table": source_table,
82
86
  "source_sys_id": source_sys_id,
@@ -87,6 +91,9 @@ def load_asset_usages(tx, *, source_table: str, source_sys_id: str) -> list[Asse
87
91
 
88
92
  def apply_asset_usage_reconciliation(tx, reconciliation: AssetUsageReconciliation) -> None:
89
93
  table = tx.table("asset_usage_index")
94
+ if not table.exists():
95
+ return
96
+
90
97
  for usage in reconciliation.to_create + reconciliation.to_refresh:
91
98
  row = _asset_usage_to_row(usage)
92
99
  table.upsert(
@@ -115,13 +122,16 @@ def resolve_asset_id_for_reference(tx, reference: AssetReference) -> str | None:
115
122
  return reference.asset_id
116
123
 
117
124
  images = tx.table("images")
125
+ if not images.exists():
126
+ return None
127
+
118
128
  for candidate_key in _candidate_storage_keys(reference.storage_key):
119
129
  row = images.find({"key": candidate_key})
120
130
  asset_id = _extract_sys_id(row)
121
131
  if asset_id:
122
132
  return asset_id
123
133
 
124
- if reference.url:
134
+ if reference.url and images.column("url").exists():
125
135
  row = images.find({"url": reference.url})
126
136
  asset_id = _extract_sys_id(row)
127
137
  if asset_id:
@@ -29,6 +29,13 @@ from typing import Any, Dict, List, Optional
29
29
  from authorizenet import apicontractsv1, apicontrollers
30
30
 
31
31
  from .base_adapter import PaymentProcessorAdapter, ProcessorError
32
+ from .authorizenet_mirror import (
33
+ sync_client_payment_account_from_authorize_account,
34
+ upsert_authorize_account_record,
35
+ upsert_authorize_customer_profile_record,
36
+ upsert_authorize_payment_profile_record,
37
+ upsert_authorize_transaction_record,
38
+ )
32
39
 
33
40
  logger = logging.getLogger(__name__)
34
41
 
@@ -132,20 +139,25 @@ class AuthorizeNetAdapter(PaymentProcessorAdapter):
132
139
  placeholder record so the routing layer can treat it uniformly.
133
140
  """
134
141
  client_id = client_data["client_id"]
135
-
136
- payment_account = tx.table("client_payment_accounts").new()
137
- payment_account["client_id"] = client_id
138
- payment_account["processor_type"] = "authorize_net"
139
- payment_account["processor_account_id"] = f"an_{client_id}"
140
- payment_account["account_status"] = "active"
141
- payment_account["charges_enabled"] = True
142
- payment_account["payouts_enabled"] = False
143
- payment_account["details_submitted"] = True
144
- payment_account["requirements_data"] = {}
145
- payment_account["capabilities"] = {
146
- "card_payments": True,
147
- "manual_settlement": True,
142
+ account = {
143
+ "client_id": client_id,
144
+ "processor_account_id": f"an_{client_id}",
145
+ "status": "active",
146
+ "charges_enabled": True,
147
+ "payouts_enabled": False,
148
+ "verification_status": "verified",
149
+ "metadata": {"account_type": "shared_merchant"},
148
150
  }
151
+ upsert_authorize_account_record(
152
+ tx,
153
+ account,
154
+ source="api:authorizenet_adapter.create_account",
155
+ )
156
+ sync_client_payment_account_from_authorize_account(
157
+ tx,
158
+ account,
159
+ client_id=client_id,
160
+ )
149
161
 
150
162
  return {
151
163
  "processor_account_id": f"an_{client_id}",
@@ -161,7 +173,7 @@ class AuthorizeNetAdapter(PaymentProcessorAdapter):
161
173
  """
162
174
  Authorize.Net shared merchant is always active if credentials are valid.
163
175
  """
164
- return {
176
+ account = {
165
177
  "account_id": processor_account_id,
166
178
  "status": "active",
167
179
  "charges_enabled": True,
@@ -172,6 +184,26 @@ class AuthorizeNetAdapter(PaymentProcessorAdapter):
172
184
  "verification_status": "verified",
173
185
  "metadata": {"account_type": "shared_merchant"},
174
186
  }
187
+ payment_account = tx.table("client_payment_accounts").find(
188
+ {
189
+ "processor_account_id": processor_account_id,
190
+ "processor_type": "authorize_net",
191
+ }
192
+ )
193
+ client_id = payment_account.get("client_id") if payment_account else None
194
+ if client_id:
195
+ account["client_id"] = client_id
196
+ upsert_authorize_account_record(
197
+ tx,
198
+ account,
199
+ source="api:authorizenet_adapter.get_account_status",
200
+ )
201
+ sync_client_payment_account_from_authorize_account(
202
+ tx,
203
+ account,
204
+ client_id=client_id,
205
+ )
206
+ return account
175
207
 
176
208
  def create_onboarding_link(
177
209
  self, tx, processor_account_id: str, return_url: str, refresh_url: str
@@ -225,12 +257,33 @@ class AuthorizeNetAdapter(PaymentProcessorAdapter):
225
257
  parts = e.error_message.split()
226
258
  for part in parts:
227
259
  if part.strip().isdigit():
260
+ upsert_authorize_customer_profile_record(
261
+ tx,
262
+ {
263
+ "customer_profile_id": part.strip(),
264
+ "email_address": email_address,
265
+ "description": customer_data.get(
266
+ "name", email_address
267
+ ),
268
+ },
269
+ source="api:authorizenet_adapter.get_or_create_customer_profile",
270
+ )
228
271
  return {
229
272
  "customer_profile_id": part.strip(),
230
273
  "created": False,
231
274
  }
232
275
  raise
233
276
 
277
+ upsert_authorize_customer_profile_record(
278
+ tx,
279
+ {
280
+ "customer_profile_id": response["customerProfileId"],
281
+ "email_address": email_address,
282
+ "description": customer_data.get("name", email_address),
283
+ },
284
+ source="api:authorizenet_adapter.get_or_create_customer_profile",
285
+ )
286
+
234
287
  return {
235
288
  "customer_profile_id": response["customerProfileId"],
236
289
  "created": True,
@@ -287,6 +340,24 @@ class AuthorizeNetAdapter(PaymentProcessorAdapter):
287
340
 
288
341
  controller = apicontrollers.createCustomerPaymentProfileController(request)
289
342
  response = self._execute(controller)
343
+ upsert_authorize_payment_profile_record(
344
+ tx,
345
+ {
346
+ "customer_profile_id": customer_profile_id,
347
+ "payment_profile_id": response["customerPaymentProfileId"],
348
+ "last4": str(payment_data.get("card_number") or "")[-4:] or None,
349
+ "expiration_date": payment_data.get("expiration_date"),
350
+ "billing_first_name": payment_data.get("first_name"),
351
+ "billing_last_name": payment_data.get("last_name"),
352
+ "billing_address": payment_data.get("address"),
353
+ "billing_city": payment_data.get("city"),
354
+ "billing_state": payment_data.get("state"),
355
+ "billing_zip": payment_data.get("zip"),
356
+ "is_default": payment_data.get("set_default", True),
357
+ "metadata": payment_data.get("metadata") or {},
358
+ },
359
+ source="api:authorizenet_adapter.attach_payment_method",
360
+ )
290
361
 
291
362
  return {
292
363
  "customer_profile_id": customer_profile_id,
@@ -326,6 +397,16 @@ class AuthorizeNetAdapter(PaymentProcessorAdapter):
326
397
  else:
327
398
  raise
328
399
 
400
+ upsert_authorize_payment_profile_record(
401
+ tx,
402
+ {
403
+ "customer_profile_id": customer_profile_id,
404
+ "payment_profile_id": payment_method_id,
405
+ "detached": True,
406
+ },
407
+ source="api:authorizenet_adapter.detach_payment_method",
408
+ )
409
+
329
410
  return {"payment_profile_id": payment_method_id, "detached": True}
330
411
 
331
412
  def delete_customer_profile(self, tx, customer_profile_id: str) -> Dict[str, Any]:
@@ -343,6 +424,12 @@ class AuthorizeNetAdapter(PaymentProcessorAdapter):
343
424
  else:
344
425
  raise
345
426
 
427
+ upsert_authorize_customer_profile_record(
428
+ tx,
429
+ {"customer_profile_id": customer_profile_id, "deleted": True},
430
+ source="api:authorizenet_adapter.delete_customer_profile",
431
+ )
432
+
346
433
  return {"customer_profile_id": customer_profile_id, "deleted": True}
347
434
 
348
435
  # ========================================================================
@@ -385,6 +472,24 @@ class AuthorizeNetAdapter(PaymentProcessorAdapter):
385
472
  response = self._execute(controller)
386
473
  trans = response.get("transactionResponse", {})
387
474
  code = int(trans.get("responseCode", 3))
475
+ status = "authorized" if RESPONSE_CODES.get(code) == "Approved" else "failed"
476
+ upsert_authorize_transaction_record(
477
+ tx,
478
+ {
479
+ "processor_transaction_id": trans.get("transId"),
480
+ "amount": float(amount_dollars),
481
+ "status": status,
482
+ "reason_for_transaction": payment_data.get("description"),
483
+ "remote_authcode": trans.get("authCode"),
484
+ "remote_responsecode": code,
485
+ "supported_id": payment_data.get("client_id"),
486
+ "supported_name": payment_data.get("description"),
487
+ "email_address": payment_data.get("customer_email")
488
+ or payment_data.get("donor_email"),
489
+ "raw_payload": response,
490
+ },
491
+ source="api:authorizenet_adapter.authorize_payment",
492
+ )
388
493
 
389
494
  if RESPONSE_CODES.get(code) == "Approved":
390
495
  return {
@@ -492,6 +597,24 @@ class AuthorizeNetAdapter(PaymentProcessorAdapter):
492
597
  response = self._execute(controller)
493
598
  trans = response.get("transactionResponse", {})
494
599
  code = int(trans.get("responseCode", 3))
600
+ status = RESPONSE_CODES.get(code, "Error")
601
+ upsert_authorize_transaction_record(
602
+ tx,
603
+ {
604
+ "processor_transaction_id": trans.get("transId"),
605
+ "amount": float(amount_dollars),
606
+ "status": status,
607
+ "reason_for_transaction": order.description if client_id or campaign else None,
608
+ "remote_authcode": trans.get("authCode"),
609
+ "remote_responsecode": code,
610
+ "supported_id": client_id or None,
611
+ "supported_name": campaign or None,
612
+ "email_address": payment_data.get("customer_email")
613
+ or payment_data.get("donor_email"),
614
+ "raw_payload": response,
615
+ },
616
+ source="api:authorizenet_adapter.charge_stored_payment_method",
617
+ )
495
618
 
496
619
  if RESPONSE_CODES.get(code) == "Approved":
497
620
  return {
@@ -581,6 +704,23 @@ class AuthorizeNetAdapter(PaymentProcessorAdapter):
581
704
  response = self._execute(controller)
582
705
  trans = response.get("transactionResponse", {})
583
706
  code = int(trans.get("responseCode", 3))
707
+ upsert_authorize_transaction_record(
708
+ tx,
709
+ {
710
+ "processor_transaction_id": trans.get("transId")
711
+ or processor_transaction_id,
712
+ "amount": float(decimal.Decimal(amount) / 100)
713
+ if amount is not None
714
+ else None,
715
+ "status": "captured"
716
+ if RESPONSE_CODES.get(code) == "Approved"
717
+ else "failed",
718
+ "remote_authcode": trans.get("authCode"),
719
+ "remote_responsecode": code,
720
+ "raw_payload": response,
721
+ },
722
+ source="api:authorizenet_adapter.capture_payment",
723
+ )
584
724
 
585
725
  if RESPONSE_CODES.get(code) == "Approved":
586
726
  captured_cents = amount if amount is not None else 0
@@ -635,6 +775,20 @@ class AuthorizeNetAdapter(PaymentProcessorAdapter):
635
775
  response = self._execute(controller)
636
776
  trans = response.get("transactionResponse", {})
637
777
  code = int(trans.get("responseCode", 3))
778
+ upsert_authorize_transaction_record(
779
+ tx,
780
+ {
781
+ "processor_transaction_id": trans.get("transId")
782
+ or processor_transaction_id,
783
+ "status": "cancelled"
784
+ if RESPONSE_CODES.get(code) == "Approved"
785
+ else "failed",
786
+ "remote_authcode": trans.get("authCode"),
787
+ "remote_responsecode": code,
788
+ "raw_payload": response,
789
+ },
790
+ source="api:authorizenet_adapter.cancel_payment",
791
+ )
638
792
 
639
793
  if RESPONSE_CODES.get(code) == "Approved":
640
794
  return {
@@ -695,6 +849,23 @@ class AuthorizeNetAdapter(PaymentProcessorAdapter):
695
849
  response = self._execute(controller)
696
850
  trans = response.get("transactionResponse", {})
697
851
  code = int(trans.get("responseCode", 3))
852
+ upsert_authorize_transaction_record(
853
+ tx,
854
+ {
855
+ "processor_transaction_id": trans.get("transId")
856
+ or processor_charge_id,
857
+ "amount": float(decimal.Decimal(amount) / 100)
858
+ if amount is not None
859
+ else None,
860
+ "status": "Refunded"
861
+ if RESPONSE_CODES.get(code) == "Approved"
862
+ else "Error",
863
+ "remote_authcode": trans.get("authCode"),
864
+ "remote_responsecode": code,
865
+ "raw_payload": response,
866
+ },
867
+ source="api:authorizenet_adapter.refund_payment",
868
+ )
698
869
 
699
870
  if RESPONSE_CODES.get(code) == "Approved":
700
871
  return {
@@ -0,0 +1,211 @@
1
+ import json
2
+ from datetime import datetime, timezone
3
+ from typing import Any, Dict, Optional
4
+
5
+
6
+ def _get_value(data: Any, key: str, default: Any = None) -> Any:
7
+ if isinstance(data, dict):
8
+ return data.get(key, default)
9
+ return getattr(data, key, default)
10
+
11
+
12
+ def _to_jsonable(value: Any) -> Any:
13
+ if value is None or isinstance(value, (str, int, float, bool)):
14
+ return value
15
+ if isinstance(value, dict):
16
+ return {str(key): _to_jsonable(item) for key, item in value.items()}
17
+ if isinstance(value, (list, tuple, set)):
18
+ return [_to_jsonable(item) for item in value]
19
+ if hasattr(value, "__dict__"):
20
+ return {
21
+ str(key): _to_jsonable(item)
22
+ for key, item in vars(value).items()
23
+ if not str(key).startswith("_")
24
+ }
25
+ return str(value)
26
+
27
+
28
+ def _json_dumps(value: Any) -> Optional[str]:
29
+ if value is None:
30
+ return None
31
+ return json.dumps(_to_jsonable(value), default=str)
32
+
33
+
34
+ def _table(tx, table_name: str):
35
+ if tx is None:
36
+ return None
37
+ try:
38
+ table = tx.table(table_name)
39
+ except Exception:
40
+ return None
41
+ try:
42
+ if hasattr(table, "exists") and not table.exists():
43
+ return None
44
+ except Exception:
45
+ pass
46
+ return table
47
+
48
+
49
+ def _upsert(tx, table_name: str, payload: Dict[str, Any], key: Dict[str, Any]):
50
+ table = _table(tx, table_name)
51
+ if not table:
52
+ return None
53
+ table.upsert(payload, key)
54
+ return payload
55
+
56
+
57
+ def _now() -> datetime:
58
+ return datetime.now(timezone.utc)
59
+
60
+
61
+ def upsert_authorize_account_record(
62
+ tx, account: Dict[str, Any], *, source: str = "api:authorizenet"
63
+ ) -> Optional[Dict[str, Any]]:
64
+ processor_account_id = _get_value(account, "processor_account_id") or _get_value(
65
+ account, "account_id"
66
+ )
67
+ if not processor_account_id:
68
+ return None
69
+ payload = {
70
+ "processor_account_id": processor_account_id,
71
+ "client_id": _get_value(account, "client_id"),
72
+ "account_status": _get_value(account, "status"),
73
+ "charges_enabled": _get_value(account, "charges_enabled"),
74
+ "payouts_enabled": _get_value(account, "payouts_enabled"),
75
+ "verification_status": _get_value(account, "verification_status"),
76
+ "metadata": _to_jsonable(_get_value(account, "metadata") or {}),
77
+ "raw_payload": _to_jsonable(account),
78
+ "sys_modified_by": source,
79
+ "sys_dirty": True,
80
+ }
81
+ return _upsert(
82
+ tx,
83
+ "authorize_accounts",
84
+ payload,
85
+ {"processor_account_id": processor_account_id},
86
+ )
87
+
88
+
89
+ def sync_client_payment_account_from_authorize_account(
90
+ tx, account: Dict[str, Any], *, client_id: Any
91
+ ):
92
+ if not client_id:
93
+ return None
94
+ processor_account_id = _get_value(account, "processor_account_id") or _get_value(
95
+ account, "account_id"
96
+ )
97
+ if not processor_account_id:
98
+ return None
99
+
100
+ payload = {
101
+ "client_id": client_id,
102
+ "processor_type": "authorize_net",
103
+ "processor_account_id": processor_account_id,
104
+ "account_status": _get_value(account, "status") or "active",
105
+ "capabilities": {
106
+ "card_payments": True,
107
+ "manual_settlement": True,
108
+ },
109
+ "configuration": {
110
+ "verification_status": _get_value(account, "verification_status"),
111
+ },
112
+ "metadata": _to_jsonable(_get_value(account, "metadata") or {}),
113
+ "onboarding_complete": True,
114
+ "charges_enabled": True,
115
+ "payouts_enabled": False,
116
+ "details_submitted": True,
117
+ }
118
+ return _upsert(
119
+ tx,
120
+ "client_payment_accounts",
121
+ payload,
122
+ {"client_id": client_id, "processor_type": "authorize_net"},
123
+ )
124
+
125
+
126
+ def upsert_authorize_customer_profile_record(
127
+ tx, customer_profile: Dict[str, Any], *, source: str = "api:authorizenet"
128
+ ) -> Optional[Dict[str, Any]]:
129
+ customer_profile_id = _get_value(customer_profile, "customer_profile_id") or _get_value(
130
+ customer_profile, "id"
131
+ )
132
+ if not customer_profile_id:
133
+ return None
134
+ payload = {
135
+ "customer_profile_id": customer_profile_id,
136
+ "email_address": _get_value(customer_profile, "email_address")
137
+ or _get_value(customer_profile, "email"),
138
+ "description": _get_value(customer_profile, "description"),
139
+ "deleted": bool(_get_value(customer_profile, "deleted")),
140
+ "metadata": _to_jsonable(_get_value(customer_profile, "metadata") or {}),
141
+ "raw_payload": _to_jsonable(customer_profile),
142
+ "lastupdateddate": _now(),
143
+ "sys_modified_by": source,
144
+ "sys_dirty": True,
145
+ }
146
+ return _upsert(
147
+ tx,
148
+ "authorize_customer_profiles",
149
+ payload,
150
+ {"customer_profile_id": customer_profile_id},
151
+ )
152
+
153
+
154
+ def upsert_authorize_payment_profile_record(
155
+ tx, payment_profile: Dict[str, Any], *, source: str = "api:authorizenet"
156
+ ) -> Optional[Dict[str, Any]]:
157
+ payment_profile_id = _get_value(payment_profile, "payment_profile_id") or _get_value(
158
+ payment_profile, "id"
159
+ )
160
+ if not payment_profile_id:
161
+ return None
162
+ payload = {
163
+ "payment_profile_id": payment_profile_id,
164
+ "customer_profile_id": _get_value(payment_profile, "customer_profile_id"),
165
+ "last4": _get_value(payment_profile, "last4"),
166
+ "expiration_date": _get_value(payment_profile, "expiration_date"),
167
+ "billing_first_name": _get_value(payment_profile, "billing_first_name"),
168
+ "billing_last_name": _get_value(payment_profile, "billing_last_name"),
169
+ "billing_address": _get_value(payment_profile, "billing_address"),
170
+ "billing_city": _get_value(payment_profile, "billing_city"),
171
+ "billing_state": _get_value(payment_profile, "billing_state"),
172
+ "billing_zip": _get_value(payment_profile, "billing_zip"),
173
+ "is_default": _get_value(payment_profile, "is_default"),
174
+ "detached": bool(_get_value(payment_profile, "detached")),
175
+ "metadata": _to_jsonable(_get_value(payment_profile, "metadata") or {}),
176
+ "raw_payload": _to_jsonable(payment_profile),
177
+ "lastupdateddate": _now(),
178
+ "sys_modified_by": source,
179
+ "sys_dirty": True,
180
+ }
181
+ return _upsert(
182
+ tx,
183
+ "authorize_payment_profiles",
184
+ payload,
185
+ {"payment_profile_id": payment_profile_id},
186
+ )
187
+
188
+
189
+ def upsert_authorize_transaction_record(
190
+ tx, transaction: Dict[str, Any], *, source: str = "api:authorizenet"
191
+ ) -> Optional[Dict[str, Any]]:
192
+ remote_transid = _get_value(transaction, "remote_transid") or _get_value(
193
+ transaction, "processor_transaction_id"
194
+ )
195
+ if not remote_transid:
196
+ return None
197
+ payload = {
198
+ "remote_transid": remote_transid,
199
+ "amount": _get_value(transaction, "amount"),
200
+ "status": _get_value(transaction, "status"),
201
+ "lastupdateddate": _now(),
202
+ "createddate": _get_value(transaction, "createddate") or _now(),
203
+ "reason_for_transaction": _get_value(transaction, "reason_for_transaction"),
204
+ "remote_authcode": _get_value(transaction, "remote_authcode"),
205
+ "remote_responsecode": _get_value(transaction, "remote_responsecode"),
206
+ "supported_id": _get_value(transaction, "supported_id"),
207
+ "supported_name": _get_value(transaction, "supported_name"),
208
+ "email_address": _get_value(transaction, "email_address"),
209
+ "reconcile_response": _json_dumps(_get_value(transaction, "raw_payload") or transaction),
210
+ }
211
+ return _upsert(tx, "authorize_transactions", payload, {"remote_transid": remote_transid})