mangopay4-python-sdk 3.57.0__tar.gz → 3.59.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 (97) hide show
  1. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/PKG-INFO +1 -1
  2. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay/constants.py +11 -0
  3. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay/fields.py +56 -1
  4. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay/resources.py +79 -3
  5. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay/utils.py +62 -4
  6. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay4_python_sdk.egg-info/PKG-INFO +1 -1
  7. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay4_python_sdk.egg-info/SOURCES.txt +2 -1
  8. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/setup.py +1 -1
  9. mangopay4_python_sdk-3.57.0/tests/resources/settlement_sample_bad.csv → mangopay4_python_sdk-3.59.0/tests/resources/settlement_sample_bad_footer.csv +1 -1
  10. mangopay4_python_sdk-3.59.0/tests/resources/settlement_sample_bad_line.csv +8 -0
  11. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_base.py +5 -1
  12. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_identity_verification.py +38 -1
  13. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_payins.py +26 -4
  14. mangopay4_python_sdk-3.59.0/tests/test_settlements.py +159 -0
  15. mangopay4_python_sdk-3.57.0/tests/test_settlements.py +0 -60
  16. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/DESCRIPTION.md +0 -0
  17. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/LICENSE +0 -0
  18. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/MANIFEST.in +0 -0
  19. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/Makefile +0 -0
  20. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/README.md +0 -0
  21. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay/__init__.py +0 -0
  22. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay/api.py +0 -0
  23. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay/auth.py +0 -0
  24. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay/base.py +0 -0
  25. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay/compat.py +0 -0
  26. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay/exceptions.py +0 -0
  27. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay/page.py +0 -0
  28. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay/query.py +0 -0
  29. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay/ratelimit.py +0 -0
  30. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay/signals.py +0 -0
  31. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay4_python_sdk.egg-info/dependency_links.txt +0 -0
  32. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay4_python_sdk.egg-info/entry_points.txt +0 -0
  33. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay4_python_sdk.egg-info/requires.txt +0 -0
  34. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/mangopay4_python_sdk.egg-info/top_level.txt +0 -0
  35. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/setup.cfg +0 -0
  36. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/__init__.py +0 -0
  37. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/card.json +0 -0
  38. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/cardregistrations.json +0 -0
  39. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/cardregistrations_update.json +0 -0
  40. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/declarative_user.json +0 -0
  41. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/legal_user.json +0 -0
  42. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/legal_user_wallet.json +0 -0
  43. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/legal_user_wallet_89.json +0 -0
  44. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/legal_user_wallet_99.json +0 -0
  45. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/list_ubo_declarations.json +0 -0
  46. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/natural_user.json +0 -0
  47. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/natural_user_wallet.json +0 -0
  48. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/natural_user_wallet_9.json +0 -0
  49. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/ubo.json +0 -0
  50. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/ubo_declaration.json +0 -0
  51. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/ubo_declaration_submit.json +0 -0
  52. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/ubo_update.json +0 -0
  53. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/user_list_2_per_page.json +0 -0
  54. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/user_list_2_per_page_page1.json +0 -0
  55. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/user_list_3_per_page_page2.json +0 -0
  56. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/user_list_full.json +0 -0
  57. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/user_list_page1.json +0 -0
  58. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/fixtures/user_wallet.json +0 -0
  59. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/images/image.jpg +0 -0
  60. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/mocks.py +0 -0
  61. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/resources/TestKycPageFile.png +0 -0
  62. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/resources/settlement_sample.csv +0 -0
  63. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/resources.py +0 -0
  64. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/settings.py +0 -0
  65. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_acquiring.py +0 -0
  66. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_bankaccounts.py +0 -0
  67. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_banking_aliases.py +0 -0
  68. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_cards.py +0 -0
  69. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_client_wallets.py +0 -0
  70. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_clients.py +0 -0
  71. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_conversion.py +0 -0
  72. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_country_authorizations.py +0 -0
  73. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_deposits.py +0 -0
  74. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_disputes.py +0 -0
  75. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_documentconsult.py +0 -0
  76. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_emoney.py +0 -0
  77. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_idempotency.py +0 -0
  78. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_kyc_document.py +0 -0
  79. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_kycs.py +0 -0
  80. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_mandates.py +0 -0
  81. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_notifications.py +0 -0
  82. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_payouts.py +0 -0
  83. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_preauthorizations.py +0 -0
  84. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_rate_limit.py +0 -0
  85. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_recipients.py +0 -0
  86. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_refunds.py +0 -0
  87. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_reports.py +0 -0
  88. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_reports_v2.py +0 -0
  89. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_repudiations.py +0 -0
  90. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_tokens.py +0 -0
  91. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_transactions.py +0 -0
  92. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_transfers.py +0 -0
  93. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_ubos.py +0 -0
  94. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_users.py +0 -0
  95. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_utils.py +0 -0
  96. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_virtual_account.py +0 -0
  97. {mangopay4_python_sdk-3.57.0 → mangopay4_python_sdk-3.59.0}/tests/test_wallets.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mangopay4-python-sdk
3
- Version: 3.57.0
3
+ Version: 3.59.0
4
4
  Summary: A client library written in python to work with mangopay v2 api
5
5
  Home-page: https://github.com/Mangopay/mangopay2-python-sdk
6
6
  Author: Mangopay (www.mangopay.com)
@@ -223,6 +223,17 @@ EVENT_TYPE_CHOICES = Choices(
223
223
  ('IDENTITY_VERIFICATION_OUTDATED', 'identity_verification_outdated', 'Identity Verification Outdated'),
224
224
  ('IDENTITY_VERIFICATION_TIMEOUT', 'identity_verification_timeout', 'Identity Verification Timeout'),
225
225
  ('IDENTITY_VERIFICATION_PENDING', 'identity_verification_pending', 'Identity Verification Pending'),
226
+ ('IDENTITY_VERIFICATION_EXPIRED', 'identity_verification_expired', 'Identity Verification Expired'),
227
+ ('IDENTITY_VERIFICATION_PENDING_PSC_ACTION', 'identity_verification_pending_psc_action',
228
+ 'Identity Verification Pending PSC Action'),
229
+ ('IDENTITY_VERIFICATION_PSC_PENDING', 'identity_verification_psc_pending',
230
+ 'Identity Verification PSC Pending'),
231
+ ('IDENTITY_VERIFICATION_PSC_VALIDATED', 'identity_verification_psc_validated',
232
+ 'Identity Verification PSC Validated'),
233
+ ('IDENTITY_VERIFICATION_PSC_REJECTED', 'identity_verification_psc_rejected',
234
+ 'Identity Verification PSC Rejected'),
235
+ ('IDENTITY_VERIFICATION_PSC_ABANDONED', 'identity_verification_psc_abandoned',
236
+ 'Identity Verification PSC Abandoned'),
226
237
 
227
238
  ('RECIPIENT_ACTIVE', 'recipient_active', 'Recipient Active'),
228
239
  ('RECIPIENT_CANCELED', 'recipient_canceled', 'Recipient Canceled'),
@@ -15,7 +15,8 @@ from .utils import timestamp_from_datetime, timestamp_from_date, Money, DebitedB
15
15
  BusinessRecipient, RecipientPropertySchema, IndividualRecipientPropertySchema, BusinessRecipientPropertySchema, \
16
16
  CompanyNumberValidation, ReportFilter, PayInIntentExternalData, PayInIntentBuyer, SupportedBank, \
17
17
  VerificationOfPayee, PayInIntentRefund, PayInIntentCapture, PayInIntentDispute, CustomFees, MarginsResponse, \
18
- UserMargin, ConsentScope, AuthenticationResult, FlowDescriptor
18
+ UserMargin, ConsentScope, AuthenticationResult, FlowDescriptor, SettlementFooterError, SettlementLineError, \
19
+ SettlementLineErrorDetail
19
20
 
20
21
 
21
22
  class FieldDescriptor(object):
@@ -1544,4 +1545,58 @@ class FlowDescriptorField(Field):
1544
1545
  result['Beneficiaries'] = value.beneficiaries
1545
1546
  return result
1546
1547
 
1548
+ return value
1549
+
1550
+
1551
+ class SettlementFooterErrorsField(Field):
1552
+ def python_value(self, value):
1553
+ if isinstance(value, list):
1554
+ return [SettlementFooterError(footer_name=item.get('FooterName', None),
1555
+ code=item.get('Code', None),
1556
+ description=item.get('Description', None))
1557
+ if isinstance(item, dict) else item
1558
+ for item in value]
1559
+
1560
+ return value
1561
+
1562
+ def api_value(self, value):
1563
+ value = super(SettlementFooterErrorsField, self).api_value(value)
1564
+
1565
+ if isinstance(value, list):
1566
+ return [item.to_api_json() if isinstance(item, SettlementFooterError) else item
1567
+ for item in value]
1568
+
1569
+ return value
1570
+
1571
+
1572
+ class SettlementLinesErrorsField(Field):
1573
+ def python_value(self, value):
1574
+ if isinstance(value, list):
1575
+ return [self._to_line_error(item) if isinstance(item, dict) else item
1576
+ for item in value]
1577
+
1578
+ return value
1579
+
1580
+ @staticmethod
1581
+ def _to_line_error(item):
1582
+ details = item.get('Details', None)
1583
+ if isinstance(details, list):
1584
+ details = [SettlementLineErrorDetail(code=detail.get('Code', None),
1585
+ description=detail.get('Description', None))
1586
+ if isinstance(detail, dict) else detail
1587
+ for detail in details]
1588
+
1589
+ return SettlementLineError(external_provider_reference=item.get('ExternalProviderReference', None),
1590
+ external_transaction_type=item.get('ExternalTransactionType', None),
1591
+ details=details)
1592
+
1593
+ def api_value(self, value):
1594
+ value = super(SettlementLinesErrorsField, self).api_value(value)
1595
+
1596
+ if isinstance(value, list):
1597
+ return [item.to_api_json() if isinstance(item, SettlementLineError) else item
1598
+ for item in value]
1599
+
1600
+ return value
1601
+
1547
1602
  return value
@@ -22,7 +22,7 @@ from .fields import (PrimaryKeyField, EmailField, CharField,
22
22
  PayInIntentExternalDataField, PayInIntentBuyerField, SupportedBanksField, VerificationOfPayeeField,
23
23
  PayInIntentRefundField, PayInIntentCaptureField, PayInIntentDisputeField, CustomFeesField,
24
24
  UserMarginField, MarginsResponseField, ConsentScopeField, AuthenticationResultField,
25
- FlowDescriptorField)
25
+ FlowDescriptorField, SettlementFooterErrorsField, SettlementLinesErrorsField)
26
26
  from .query import InsertQuery, UpdateQuery, SelectQuery, ActionQuery, DeleteQuery
27
27
 
28
28
 
@@ -214,6 +214,13 @@ class UserConsent(BaseModel):
214
214
 
215
215
  @python_2_unicode_compatible
216
216
  class NaturalUser(User):
217
+ """
218
+ .. deprecated::
219
+ The non-SCA natural user endpoints are deprecated. Use :class:`NaturalUserSca`
220
+ (create/update via ``/sca/users/natural``) and :meth:`User.get_sca` for
221
+ retrieval instead. Kept for backward compatibility; will be removed in a
222
+ future major version.
223
+ """
217
224
  person_type = CharField(api_name='PersonType',
218
225
  choices=constants.USER_TYPE_CHOICES,
219
226
  default=constants.USER_TYPE_CHOICES.natural,
@@ -297,6 +304,13 @@ class NaturalUserSca(User):
297
304
 
298
305
  @python_2_unicode_compatible
299
306
  class LegalUser(User):
307
+ """
308
+ .. deprecated::
309
+ The non-SCA legal user endpoints are deprecated. Use :class:`LegalUserSca`
310
+ (create/update via ``/sca/users/legal``) and :meth:`User.get_sca` for
311
+ retrieval instead. Kept for backward compatibility; will be removed in a
312
+ future major version.
313
+ """
300
314
  person_type = CharField(api_name='PersonType',
301
315
  choices=constants.USER_TYPE_CHOICES,
302
316
  default=constants.USER_TYPE_CHOICES.legal,
@@ -1870,12 +1884,22 @@ class CardPreAuthorizedDepositPayIn(BaseModel):
1870
1884
  return insert.execute()
1871
1885
 
1872
1886
  def create_prior_to_complement(self, idempotency_key=None, **kwargs):
1887
+ """
1888
+ .. deprecated::
1889
+ The capture-with-complement flow is deprecated and will be removed in a
1890
+ future major version. Use :meth:`create_without_complement` instead.
1891
+ """
1873
1892
  insert = InsertQuery(self, idempotency_key, **kwargs)
1874
1893
  insert.identifier = 'CREATE_PRIOR_TO_COMPLEMENT'
1875
1894
  insert.insert_query = self.get_field_dict()
1876
1895
  return insert.execute()
1877
1896
 
1878
1897
  def create_complement(self, idempotency_key=None, **kwargs):
1898
+ """
1899
+ .. deprecated::
1900
+ The complement flow is deprecated and will be removed in a future major
1901
+ version. Use :meth:`create_without_complement` instead.
1902
+ """
1879
1903
  insert = InsertQuery(self, idempotency_key, **kwargs)
1880
1904
  insert.identifier = 'CREATE_COMPLEMENT'
1881
1905
  insert.insert_query = self.get_field_dict()
@@ -1921,12 +1945,22 @@ class PreAuthorizedDepositPayIn(BaseModel):
1921
1945
  return insert.execute()
1922
1946
 
1923
1947
  def create_prior_to_complement(self, idempotency_key=None, **kwargs):
1948
+ """
1949
+ .. deprecated::
1950
+ The capture-with-complement flow is deprecated and will be removed in a
1951
+ future major version. Use :meth:`create_without_complement` instead.
1952
+ """
1924
1953
  insert = InsertQuery(self, idempotency_key, **kwargs)
1925
1954
  insert.identifier = 'CREATE_PRIOR_TO_COMPLEMENT'
1926
1955
  insert.insert_query = self.get_field_dict()
1927
1956
  return insert.execute()
1928
1957
 
1929
1958
  def create_complement(self, idempotency_key=None, **kwargs):
1959
+ """
1960
+ .. deprecated::
1961
+ The complement flow is deprecated and will be removed in a future major
1962
+ version. Use :meth:`create_without_complement` instead.
1963
+ """
1930
1964
  insert = InsertQuery(self, idempotency_key, **kwargs)
1931
1965
  insert.identifier = 'CREATE_COMPLEMENT'
1932
1966
  insert.insert_query = self.get_field_dict()
@@ -3268,6 +3302,7 @@ class IdentityVerification(BaseModel):
3268
3302
  last_update = DateTimeField(api_name='UpdateDate')
3269
3303
  user_id = CharField(api_name='UserId')
3270
3304
  checks = ListField(api_name='Checks')
3305
+ pscs = ListField(api_name='PSCs')
3271
3306
 
3272
3307
  class Meta:
3273
3308
  verbose_name = 'identity_verification'
@@ -3293,6 +3328,40 @@ class IdentityVerification(BaseModel):
3293
3328
  return select.all(*args, **kwargs)
3294
3329
 
3295
3330
 
3331
+ class Psc(BaseModel):
3332
+ first_name = CharField(api_name='FirstName')
3333
+ last_name = CharField(api_name='LastName')
3334
+ email = CharField(api_name='Email')
3335
+ date_of_birth = DateField(api_name='DateOfBirth')
3336
+ country = CharField(api_name='Country')
3337
+ nationality = CharField(api_name='Nationality')
3338
+ psc_type = CharField(api_name='PscType')
3339
+ status = CharField(api_name='Status')
3340
+ validation_date = DateTimeField(api_name='ValidationDate')
3341
+ hosted_url = CharField(api_name='HostedUrl')
3342
+ data = ListField(api_name='Data')
3343
+ last_update = DateTimeField(api_name='LastUpdate')
3344
+
3345
+ class Meta:
3346
+ verbose_name = 'psc'
3347
+ verbose_name_plural = 'pscs'
3348
+
3349
+ url = {
3350
+ 'RETRY': '/identity-verifications/%(identity_verification_id)s/pscs/%(psc_id)s/retry'
3351
+ }
3352
+
3353
+ @classmethod
3354
+ def retry(cls, identity_verification_id, psc_id, **kwargs):
3355
+ """Request the retry of a PSC session.
3356
+
3357
+ The keyword arguments can be empty or contain the values to be updated.
3358
+ """
3359
+ path_params = {'identity_verification_id': identity_verification_id, 'psc_id': psc_id}
3360
+ update = UpdateQuery(Psc, None, path_params, **kwargs)
3361
+ update.identifier = 'RETRY'
3362
+ return update.execute()
3363
+
3364
+
3296
3365
  class RecipientSchema(BaseModel):
3297
3366
  display_name = RecipientPropertySchemaField(api_name='DisplayName')
3298
3367
  payout_method_type = RecipientPropertySchemaField(api_name='PayoutMethodType')
@@ -3391,6 +3460,7 @@ class PayInIntent(BaseModel):
3391
3460
  'REVERSE_REFUND': '/payins/intents/%(intent_id)s/refunds/%(refund_id)s/reverse',
3392
3461
  'CREATE_DISPUTE': '/payins/intents/%(intent_id)s/captures/%(capture_id)s/disputes',
3393
3462
  'UPDATE_DISPUTE_OUTCOME': '/payins/intents/%(intent_id)s/captures/%(capture_id)s/disputes/%(dispute_id)s/decision',
3463
+ 'GET_EXTERNAL_PROVIDERS_LIST': '/payins/intents/external-providers',
3394
3464
  }
3395
3465
 
3396
3466
  def create_authorization(self, idempotency_key=None, **kwargs):
@@ -3447,6 +3517,12 @@ class PayInIntent(BaseModel):
3447
3517
  update.identifier = 'UPDATE_DISPUTE_OUTCOME'
3448
3518
  return update.execute(is_v3=True)
3449
3519
 
3520
+ @classmethod
3521
+ def get_external_providers_list(cls, *args, **kwargs):
3522
+ select = SelectQuery(PayInIntent, *args, **kwargs)
3523
+ select.identifier = 'GET_EXTERNAL_PROVIDERS_LIST'
3524
+ return select.get("", is_v3=True, strict_dict_parsing=False, *args, **kwargs)
3525
+
3450
3526
 
3451
3527
  class Settlement(BaseModel):
3452
3528
  settlement_id = CharField(api_name='SettlementId')
@@ -3498,8 +3574,8 @@ class Settlement(BaseModel):
3498
3574
 
3499
3575
 
3500
3576
  class SettlementValidation(BaseModel):
3501
- footer_errors = ListField(api_name='FooterErrors')
3502
- lines_errors = ListField(api_name='LinesErrors')
3577
+ footer_errors = SettlementFooterErrorsField(api_name='FooterErrors')
3578
+ lines_errors = SettlementLinesErrorsField(api_name='LinesErrors')
3503
3579
 
3504
3580
  class Meta:
3505
3581
  verbose_name = 'settlement_validation'
@@ -1521,7 +1521,7 @@ class PayInIntentSeller(object):
1521
1521
  @add_camelcase_aliases
1522
1522
  class IntentSplit(object):
1523
1523
  def __init__(self, line_item_id=None, wallet_id=None, seller_id=None, split_amount=None, fees_amount=None,
1524
- transfer_date=None, description=None, status=None):
1524
+ transfer_date=None, description=None, status=None, tag=None):
1525
1525
  self.line_item_id = line_item_id
1526
1526
  self.wallet_id = wallet_id
1527
1527
  self.seller_id = seller_id
@@ -1530,11 +1530,12 @@ class IntentSplit(object):
1530
1530
  self.description = description
1531
1531
  self.status = status
1532
1532
  self.transfer_date = transfer_date
1533
+ self.tag = tag
1533
1534
 
1534
1535
  def __str__(self):
1535
- return 'PayInIntentSplit: %s %s %s %s %s %s %s %s' % \
1536
+ return 'PayInIntentSplit: %s %s %s %s %s %s %s %s %s' % \
1536
1537
  (self.line_item_id, self.wallet_id, self.seller_id, self.split_amount, self.fees_amount, self.description,
1537
- self.status, self.transfer_date)
1538
+ self.status, self.transfer_date, self.tag)
1538
1539
 
1539
1540
  def to_api_json(self):
1540
1541
  return {
@@ -1545,7 +1546,8 @@ class IntentSplit(object):
1545
1546
  "FeesAmount": self.fees_amount,
1546
1547
  "Description": self.description,
1547
1548
  "Status": self.status,
1548
- "TransferDate": self.transfer_date
1549
+ "TransferDate": self.transfer_date,
1550
+ "Tag": self.tag
1549
1551
  }
1550
1552
 
1551
1553
 
@@ -1754,4 +1756,60 @@ class FlowDescriptor(object):
1754
1756
  return {
1755
1757
  "FlowId": self.flow_id,
1756
1758
  "Beneficiaries": self.beneficiaries
1759
+ }
1760
+
1761
+
1762
+ @add_camelcase_aliases
1763
+ class SettlementFooterError(object):
1764
+ def __init__(self, footer_name=None, code=None, description=None):
1765
+ self.footer_name = footer_name
1766
+ self.code = code
1767
+ self.description = description
1768
+
1769
+ def __str__(self):
1770
+ return 'SettlementFooterError: %s %s %s' % (self.footer_name, self.code, self.description)
1771
+
1772
+ def to_api_json(self):
1773
+ return {
1774
+ "FooterName": self.footer_name,
1775
+ "Code": self.code,
1776
+ "Description": self.description
1777
+ }
1778
+
1779
+
1780
+ @add_camelcase_aliases
1781
+ class SettlementLineErrorDetail(object):
1782
+ def __init__(self, code=None, description=None):
1783
+ self.code = code
1784
+ self.description = description
1785
+
1786
+ def __str__(self):
1787
+ return 'SettlementLineErrorDetail: %s %s' % (self.code, self.description)
1788
+
1789
+ def to_api_json(self):
1790
+ return {
1791
+ "Code": self.code,
1792
+ "Description": self.description
1793
+ }
1794
+
1795
+
1796
+ @add_camelcase_aliases
1797
+ class SettlementLineError(object):
1798
+ def __init__(self, external_provider_reference=None, external_transaction_type=None, details=None):
1799
+ self.external_provider_reference = external_provider_reference
1800
+ self.external_transaction_type = external_transaction_type
1801
+ self.details = details
1802
+
1803
+ def __str__(self):
1804
+ return 'SettlementLineError: %s %s' % (self.external_provider_reference, self.external_transaction_type)
1805
+
1806
+ def to_api_json(self):
1807
+ details = self.details
1808
+ if isinstance(details, list):
1809
+ details = [d.to_api_json() if isinstance(d, SettlementLineErrorDetail) else d for d in details]
1810
+
1811
+ return {
1812
+ "ExternalProviderReference": self.external_provider_reference,
1813
+ "ExternalTransactionType": self.external_transaction_type,
1814
+ "Details": details
1757
1815
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mangopay4-python-sdk
3
- Version: 3.57.0
3
+ Version: 3.59.0
4
4
  Summary: A client library written in python to work with mangopay v2 api
5
5
  Home-page: https://github.com/Mangopay/mangopay2-python-sdk
6
6
  Author: Mangopay (www.mangopay.com)
@@ -91,4 +91,5 @@ tests/fixtures/user_wallet.json
91
91
  tests/images/image.jpg
92
92
  tests/resources/TestKycPageFile.png
93
93
  tests/resources/settlement_sample.csv
94
- tests/resources/settlement_sample_bad.csv
94
+ tests/resources/settlement_sample_bad_footer.csv
95
+ tests/resources/settlement_sample_bad_line.csv
@@ -10,7 +10,7 @@ with open(path.join(here, 'DESCRIPTION.md'), encoding='utf-8') as f:
10
10
 
11
11
  setup(
12
12
  name='mangopay4-python-sdk',
13
- version='3.57.0',
13
+ version='3.59.0',
14
14
  description='A client library written in python to work with mangopay v2 api',
15
15
  long_description='This SDK is a client library for interacting with the Mangopay API.',
16
16
  url='https://github.com/Mangopay/mangopay2-python-sdk',
@@ -5,4 +5,4 @@ SettlementDate,19-06-2025,,,,,,,
5
5
  ExternalProviderName,Paypal,,,,,,,
6
6
  TotalSettlementFeesAmount,0,,,,,,,
7
7
  TotalNetSettlementAmount,99999,,,,,,,
8
- SettlementCurrency,EUR,,,,,,
8
+ SettlementCurrency,EURR,,,,,,
@@ -0,0 +1,8 @@
1
+ ExternalProviderReference,ExternalPaymentMethod,ExternalTransactionType,ExternalTransactionStatus,ExternalProcessingDate,Amount,Currency,ExternalInitialReference,ExternalProviderFees
2
+ 6TU984332A894613W,PAYPAL,PAYMENT,SETTLED,191-06-2025,1000,EUR,,0
3
+ ,,,,,,,,
4
+ SettlementDate,19-06-2025,,,,,,,
5
+ ExternalProviderName,Paypal,,,,,,,
6
+ TotalSettlementFeesAmount,0,,,,,,,
7
+ TotalNetSettlementAmount,99999,,,,,,,
8
+ SettlementCurrency,EUR,,,,,,
@@ -873,6 +873,7 @@ class BaseTestLive(unittest.TestCase):
873
873
  intent_authorization.external_data = external_data
874
874
  intent_authorization.line_items = line_items
875
875
  intent_authorization.buyer = buyer
876
+ intent_authorization.tag = 'Intent tag'
876
877
  return PayInIntent(**intent_authorization.create_authorization())
877
878
 
878
879
  @staticmethod
@@ -908,11 +909,13 @@ class BaseTestLive(unittest.TestCase):
908
909
  intent_authorization.external_data = external_data
909
910
  intent_authorization.line_items = line_items
910
911
  intent_authorization.buyer = buyer
912
+ intent_authorization.tag = 'Intent tag'
911
913
 
912
914
  intent = PayInIntent(**intent_authorization.create_authorization())
913
915
 
914
916
  full_capture = PayInIntent()
915
917
  full_capture.external_data = external_data
918
+ full_capture.tag = 'Intent tag'
916
919
 
917
920
  return PayInIntent(**full_capture.create_capture(intent.id))
918
921
 
@@ -928,7 +931,8 @@ class BaseTestLive(unittest.TestCase):
928
931
  external_data.external_provider_payment_method = 'PAYPAL'
929
932
 
930
933
  refund_dto = {
931
- 'external_data': external_data
934
+ 'external_data': external_data,
935
+ 'tag': 'Intent tag'
932
936
  }
933
937
  return PayInIntent(**PayInIntent.create_refund(full_capture.id, **refund_dto))
934
938
 
@@ -1,9 +1,14 @@
1
- from mangopay.resources import IdentityVerification
1
+ import unittest
2
+ import uuid
3
+
4
+ from mangopay.resources import IdentityVerification, Psc
2
5
  from tests.test_base import BaseTestLive
3
6
 
4
7
 
5
8
  class IdentityVerificationTest(BaseTestLive):
6
9
  _identity_verification = None
10
+ _idv_id_with_pscs = 'idnver_01KZXBHDH1DWC887PW6T46NVTV'
11
+ _psc_id = 'psc_x_01KZXBT5F8FGGSK9JXGCYMNWRP'
7
12
 
8
13
  def test_create_identity_verification(self):
9
14
  self.create_new_identity_verification()
@@ -30,6 +35,38 @@ class IdentityVerificationTest(BaseTestLive):
30
35
  self.assertIsInstance(fetched.data, list)
31
36
  self.assertTrue(len(fetched.data) > 0)
32
37
 
38
+ def test_get_identity_verification_with_pscs(self):
39
+ fetched = IdentityVerification.get(IdentityVerificationTest._idv_id_with_pscs)
40
+
41
+ self.assertIsNotNone(fetched)
42
+ self.assertIsNotNone(fetched.pscs)
43
+ self.assertTrue(len(fetched.pscs) > 0)
44
+ self.assertTrue(len(fetched.pscs[0]['Data']) > 0)
45
+
46
+ @unittest.skip("PSC must be manually invalidated before testing")
47
+ def test_retry_psc_without_update_params(self):
48
+ psc = Psc(**Psc.retry(IdentityVerificationTest._idv_id_with_pscs, IdentityVerificationTest._psc_id))
49
+
50
+ self.assertIsNotNone(psc)
51
+ self.assertIsNotNone(psc.hosted_url)
52
+ self.assertEqual('PENDING_VALIDATION', psc.status)
53
+
54
+ @unittest.skip("PSC must be manually invalidated before testing")
55
+ def test_retry_psc_with_update_params(self):
56
+ first_name = str(uuid.uuid4())
57
+ last_name = str(uuid.uuid4())
58
+
59
+ psc = Psc(**Psc.retry(IdentityVerificationTest._idv_id_with_pscs,
60
+ IdentityVerificationTest._psc_id,
61
+ first_name=first_name,
62
+ last_name=last_name))
63
+
64
+ self.assertIsNotNone(psc)
65
+ self.assertIsNotNone(psc.hosted_url)
66
+ self.assertEqual('PENDING_VALIDATION', psc.status)
67
+ self.assertEqual(first_name, psc.first_name)
68
+ self.assertEqual(last_name, psc.last_name)
69
+
33
70
  @staticmethod
34
71
  def create_new_identity_verification():
35
72
  if IdentityVerificationTest._identity_verification is None:
@@ -738,6 +738,7 @@ def create_new_splits(intent):
738
738
  split = IntentSplit()
739
739
  split.line_item_id = intent.line_items[0]['Id']
740
740
  split.split_amount = 10
741
+ split.tag = 'custom tag'
741
742
 
742
743
  intent_splits = PayInIntentSplits()
743
744
  intent_splits.splits = [split]
@@ -2343,16 +2344,19 @@ class PayInsTestLive(BaseTestLive):
2343
2344
  self.assertEqual('AUTHORIZED', created.status)
2344
2345
  self.assertIsNotNone(created.unfunded_amount)
2345
2346
  self.assertIsNotNone(created.line_items[0]['UnfundedSellerAmount'])
2347
+ self.assertEqual('Intent tag', created.tag)
2346
2348
 
2347
2349
  def test_create_pay_in_intent_full_capture(self):
2348
2350
  created = BaseTestLive.create_new_pay_in_intent_full_capture()
2349
2351
 
2350
2352
  self.assertIsNotNone(created)
2351
2353
  self.assertEqual('CAPTURED', created.status)
2354
+ self.assertEqual('Intent tag', created.tag)
2352
2355
 
2353
2356
  def test_create_full_pay_in_intent_refund(self):
2354
2357
  refund = BaseTestLive.create_new_full_pay_in_intent_refund()
2355
2358
  self.assertEqual('REFUNDED', refund.status)
2359
+ self.assertEqual('Intent tag', refund.tag)
2356
2360
 
2357
2361
  def test_create_partial_pay_in_intent_refund(self):
2358
2362
  full_capture = BaseTestLive.create_new_pay_in_intent_full_capture()
@@ -2367,6 +2371,7 @@ class PayInsTestLive(BaseTestLive):
2367
2371
  refund_dto = {
2368
2372
  'external_data': external_data,
2369
2373
  'amount': 1000,
2374
+ 'tag': 'Intent tag',
2370
2375
  'line_items': [
2371
2376
  {
2372
2377
  'id': full_capture.line_items[0]['Id'],
@@ -2376,6 +2381,7 @@ class PayInsTestLive(BaseTestLive):
2376
2381
  }
2377
2382
  refund = PayInIntent(**PayInIntent.create_refund(full_capture.id, **refund_dto))
2378
2383
  self.assertEqual('REFUNDED', refund.status)
2384
+ self.assertEqual('Intent tag', refund.tag)
2379
2385
 
2380
2386
  def test_fully_reverse_pay_in_intent_refund(self):
2381
2387
  refund = BaseTestLive.create_new_full_pay_in_intent_refund()
@@ -2426,9 +2432,9 @@ class PayInsTestLive(BaseTestLive):
2426
2432
  external_data = PayInIntentExternalData()
2427
2433
  external_data.external_processing_date = '01-10-2026'
2428
2434
  external_data.external_provider_reference = ''.join(random.choices(string.ascii_letters + string.digits, k=10))
2429
- external_data.external_merchant_reference = 'Order-xyz-35e8490e-2ec9-4c82-978e-c712a3f5ba16'
2435
+ # external_data.external_merchant_reference = 'Order-xyz-35e8490e-2ec9-4c82-978e-c712a3f5ba16'
2430
2436
  external_data.external_provider_name = 'Stripe'
2431
- external_data.external_provider_payment_method = 'PAYPAL'
2437
+ # external_data.external_provider_payment_method = 'PAYPAL'
2432
2438
 
2433
2439
  line_item = PayInIntentLineItem()
2434
2440
  line_item.id = intent.line_items[0]['Id']
@@ -2452,6 +2458,7 @@ class PayInsTestLive(BaseTestLive):
2452
2458
  fetched = PayInIntent.get(intent.id)
2453
2459
  self.assertIsNotNone(fetched)
2454
2460
  self.assertEqual(fetched.status, intent.status)
2461
+ self.assertEqual(intent.tag, fetched.tag)
2455
2462
 
2456
2463
  def test_cancel_pay_in_intent(self):
2457
2464
  intent = BaseTestLive.create_new_pay_in_intent_authorization()
@@ -2464,12 +2471,19 @@ class PayInsTestLive(BaseTestLive):
2464
2471
  cancelled = PayInIntent(**PayInIntent.cancel(intent.id, **cancel_details))
2465
2472
  self.assertEqual(cancelled.status, 'CANCELLED')
2466
2473
 
2474
+ def test_get_external_providers_list(self):
2475
+ providers = PayInIntent.get_external_providers_list()
2476
+ self.assertIsNotNone(providers)
2477
+ self.assertTrue(len(providers) > 0)
2478
+ self.assertIn('Mangopay', providers)
2479
+
2467
2480
  def test_create_pay_in_intent_splits(self):
2468
2481
  intent = BaseTestLive.create_new_pay_in_intent_authorization()
2469
2482
  created_splits = create_new_splits(intent)
2470
2483
 
2471
2484
  self.assertIsNotNone(created_splits)
2472
2485
  self.assertEqual('CREATED', created_splits.splits[0]['Status'])
2486
+ self.assertIsNotNone(created_splits.splits[0]['Tag'])
2473
2487
 
2474
2488
  def test_execute_split(self):
2475
2489
  intent = BaseTestLive.create_new_pay_in_intent_authorization()
@@ -2494,6 +2508,7 @@ class PayInsTestLive(BaseTestLive):
2494
2508
  created_splits = create_new_splits(intent)
2495
2509
  fetched = PayInIntentSplit.get(intent.id, created_splits.splits[0]['Id'])
2496
2510
  self.assertEqual('CREATED', fetched.status)
2511
+ self.assertIsNotNone(fetched.tag)
2497
2512
 
2498
2513
  def test_update_split(self):
2499
2514
  intent = BaseTestLive.create_new_pay_in_intent_authorization()
@@ -2503,10 +2518,12 @@ class PayInsTestLive(BaseTestLive):
2503
2518
  created_splits.splits[0]['Id'],
2504
2519
  **{
2505
2520
  'description': 'updated description',
2506
- 'line_item_id': created_splits.splits[0]['LineItemId']
2521
+ 'line_item_id': created_splits.splits[0]['LineItemId'],
2522
+ 'tag': 'updated tag'
2507
2523
  }
2508
2524
  )
2509
2525
  self.assertEqual('updated description', updated['description'])
2526
+ self.assertEqual('updated tag', updated['tag'])
2510
2527
 
2511
2528
  def test_create_client_bank_wire_direct_payin(self):
2512
2529
  pay_in = ClientBankWireDirectPayIn()
@@ -2519,6 +2536,7 @@ class PayInsTestLive(BaseTestLive):
2519
2536
  self.assertEqual('BANK_WIRE', created.payment_type)
2520
2537
  self.assertEqual('DIRECT', created.execution_type)
2521
2538
 
2539
+ @unittest.skip("can't be tested. API returning 500")
2522
2540
  def test_paypal_data_collection(self):
2523
2541
  data = {
2524
2542
  "sender_account_id": "A12345N343",
@@ -2574,10 +2592,12 @@ class PayInsTestLive(BaseTestLive):
2574
2592
  external_data.external_provider_payment_method = 'PAYPAL'
2575
2593
 
2576
2594
  create_dto = {
2577
- 'external_data': external_data
2595
+ 'external_data': external_data,
2596
+ 'tag': 'Intent tag'
2578
2597
  }
2579
2598
  dispute = PayInIntent(**PayInIntent.create_dispute(full_capture.id, full_capture.capture.id, **create_dto))
2580
2599
  self.assertEqual('DISPUTED', dispute.status)
2600
+ self.assertEqual('Intent tag', dispute.tag)
2581
2601
 
2582
2602
  def test_create_partial_pay_in_intent_dispute(self):
2583
2603
  full_capture = BaseTestLive.create_new_pay_in_intent_full_capture()
@@ -2592,6 +2612,7 @@ class PayInsTestLive(BaseTestLive):
2592
2612
  create_dto = {
2593
2613
  'external_data': external_data,
2594
2614
  'amount': 1000,
2615
+ 'tag': 'Intent tag',
2595
2616
  'line_items': [
2596
2617
  {
2597
2618
  'id': full_capture.line_items[0]['Id'],
@@ -2601,6 +2622,7 @@ class PayInsTestLive(BaseTestLive):
2601
2622
  }
2602
2623
  dispute = PayInIntent(**PayInIntent.create_dispute(full_capture.id, full_capture.capture.id, **create_dto))
2603
2624
  self.assertEqual('DISPUTED', dispute.status)
2625
+ self.assertEqual('Intent tag', dispute.tag)
2604
2626
 
2605
2627
  def test_update_intent_dispute_outcome(self):
2606
2628
  full_capture = BaseTestLive.create_new_pay_in_intent_full_capture()
@@ -0,0 +1,159 @@
1
+ # -*- coding: utf-8 -*-
2
+ import os
3
+ import time
4
+
5
+ import requests
6
+ import responses
7
+
8
+ from mangopay.resources import Settlement, SettlementValidation
9
+ from mangopay.utils import SettlementFooterError, SettlementLineError, SettlementLineErrorDetail
10
+ from tests import settings
11
+ from tests.test_base import BaseTest, BaseTestLive
12
+
13
+
14
+ class SettlementTestLive(BaseTestLive):
15
+ _settlement = None
16
+
17
+ def test_generate_upload_url(self):
18
+ settlement = self.create_new_settlement()
19
+ self.assertIsNotNone(settlement.settlement_id)
20
+ self.assertIsNotNone(settlement.upload_url)
21
+
22
+ def test_get_settlement(self):
23
+ settlement = self.create_new_settlement()
24
+ fetched = Settlement.get(settlement.settlement_id)
25
+ self.assertEqual('PENDING_UPLOAD', fetched.status)
26
+
27
+ def test_generate_new_upload_url(self):
28
+ settlement = self.create_new_settlement()
29
+ new_settlement = Settlement(id=settlement.settlement_id, file_name='updated_settlement_sample.csv')
30
+ result = Settlement(**new_settlement.generate_new_upload_url())
31
+ self.assertIsNotNone(result.settlement_id)
32
+ self.assertIsNotNone(result.upload_url)
33
+
34
+ def test_upload_file(self):
35
+ settlement, response = self.upload_settlement_file('settlement_sample.csv')
36
+ self.assertEqual(200, response.status_code)
37
+
38
+ def test_get_validations_footer_errors(self):
39
+ settlement, response = self.upload_settlement_file('settlement_sample_bad_footer.csv')
40
+ time.sleep(20)
41
+ validations = SettlementValidation.get(settlement.settlement_id, page=0, per_page=10)
42
+ self.assertIsNotNone(validations)
43
+ self.assertIsNotNone(validations.footer_errors)
44
+
45
+ footer_error = validations.footer_errors[0]
46
+ self.assertIsNotNone(footer_error.footer_name)
47
+ self.assertIsNotNone(footer_error.code)
48
+ self.assertIsNotNone(footer_error.description)
49
+
50
+ def test_get_validations_line_errors(self):
51
+ settlement, response = self.upload_settlement_file('settlement_sample_bad_line.csv')
52
+ time.sleep(20)
53
+ validations = SettlementValidation.get(settlement.settlement_id, page=0, per_page=10)
54
+ self.assertIsNotNone(validations)
55
+ self.assertIsNotNone(validations.lines_errors)
56
+
57
+ line_error = validations.lines_errors[0]
58
+ self.assertIsNotNone(line_error.external_provider_reference)
59
+ self.assertIsNotNone(line_error.external_transaction_type)
60
+ self.assertIsNotNone(line_error.details)
61
+
62
+ line_error_detail = line_error.details[0]
63
+ self.assertIsNotNone(line_error_detail.code)
64
+ self.assertIsNotNone(line_error_detail.description)
65
+
66
+ def test_cancel_settlement(self):
67
+ settlement, response = self.upload_settlement_file('settlement_sample_bad_line.csv')
68
+ cancelled = Settlement.cancel(settlement.settlement_id)
69
+ self.assertIsNotNone(cancelled)
70
+ self.assertEqual("CANCELLED", cancelled['status'])
71
+
72
+ @staticmethod
73
+ def create_new_settlement():
74
+ if SettlementTestLive._settlement is None:
75
+ settlement = Settlement(file_name='settlement_sample.csv')
76
+ SettlementTestLive._settlement = Settlement(**settlement.generate_upload_url())
77
+ return SettlementTestLive._settlement
78
+
79
+ def upload_settlement_file(self, file_name):
80
+ settlement = Settlement(file_name=file_name)
81
+ settlement = Settlement(**settlement.generate_upload_url())
82
+ file_path = os.path.join(os.path.dirname(__file__), 'resources', file_name)
83
+ with open(file_path, 'rb') as f:
84
+ data = f.read()
85
+
86
+ headers = {'Content-Type': 'text/csv'}
87
+ response = requests.put(settlement.upload_url, data=data, headers=headers)
88
+ return settlement, response
89
+
90
+
91
+ class SettlementValidationTest(BaseTest):
92
+ @responses.activate
93
+ def test_get_validations_dto(self):
94
+ settlement_id = "settlement_12345"
95
+ v3_url = settings.MANGOPAY_API_SANDBOX_URL.replace('v2.01', 'V3.0')
96
+ self.register_mock({
97
+ 'method': responses.GET,
98
+ 'url': v3_url + settings.MANGOPAY_CLIENT_ID +
99
+ '/payins/intents/settlements/' + settlement_id + '/validations',
100
+ 'body': {
101
+ "FooterErrors": [
102
+ {
103
+ "FooterName": "DeclaredIntentAmount",
104
+ "Code": "TOTAL_NET_MISCALCULATED",
105
+ "Description": "Declared intent amount does not match actual settlement amount"
106
+ }
107
+ ],
108
+ "LinesErrors": [
109
+ {
110
+ "ExternalProviderReference": "d3f7a1b2-4c5e-6789-abcd-ef0123456789",
111
+ "ExternalTransactionType": "REFUND",
112
+ "Details": [
113
+ {
114
+ "Code": "MATCH_REF_NOT_FOUND",
115
+ "Description": "ExternalProviderReference not found for any existing Intent in the system"
116
+ },
117
+ {
118
+ "Code": "FORMAT_DATE_INVALID",
119
+ "Description": "ExternalProcessingDate format is invalid. Expected DD-MM-YYYY"
120
+ }
121
+ ]
122
+ }
123
+ ]
124
+ },
125
+ 'status': 200
126
+ })
127
+
128
+ validations = SettlementValidation.get(settlement_id, page=1, per_page=10)
129
+
130
+ self.assertIsNotNone(validations)
131
+ self.assertIsNotNone(validations.footer_errors)
132
+ self.assertIsNotNone(validations.lines_errors)
133
+
134
+ self.assertEqual(1, len(validations.footer_errors))
135
+ footer_error = validations.footer_errors[0]
136
+ self.assertIsInstance(footer_error, SettlementFooterError)
137
+ self.assertEqual("DeclaredIntentAmount", footer_error.footer_name)
138
+ self.assertEqual("TOTAL_NET_MISCALCULATED", footer_error.code)
139
+ self.assertEqual("Declared intent amount does not match actual settlement amount",
140
+ footer_error.description)
141
+
142
+ self.assertEqual(1, len(validations.lines_errors))
143
+ line_error = validations.lines_errors[0]
144
+ self.assertIsInstance(line_error, SettlementLineError)
145
+ self.assertEqual("d3f7a1b2-4c5e-6789-abcd-ef0123456789", line_error.external_provider_reference)
146
+ self.assertEqual("REFUND", line_error.external_transaction_type)
147
+ self.assertIsNotNone(line_error.details)
148
+ self.assertEqual(2, len(line_error.details))
149
+
150
+ first_detail = line_error.details[0]
151
+ self.assertIsInstance(first_detail, SettlementLineErrorDetail)
152
+ self.assertEqual("MATCH_REF_NOT_FOUND", first_detail.code)
153
+ self.assertEqual("ExternalProviderReference not found for any existing Intent in the system",
154
+ first_detail.description)
155
+
156
+ second_detail = line_error.details[1]
157
+ self.assertEqual("FORMAT_DATE_INVALID", second_detail.code)
158
+ self.assertEqual("ExternalProcessingDate format is invalid. Expected DD-MM-YYYY",
159
+ second_detail.description)
@@ -1,60 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- import os
3
- import requests
4
-
5
- from mangopay.resources import Settlement, SettlementValidation
6
- from tests.test_base import BaseTestLive
7
-
8
-
9
- class SettlementTestLive(BaseTestLive):
10
- _settlement = None
11
-
12
- def test_generate_upload_url(self):
13
- settlement = self.create_new_settlement()
14
- self.assertIsNotNone(settlement.settlement_id)
15
- self.assertIsNotNone(settlement.upload_url)
16
-
17
- def test_get_settlement(self):
18
- settlement = self.create_new_settlement()
19
- fetched = Settlement.get(settlement.settlement_id)
20
- self.assertEqual('PENDING_UPLOAD', fetched.status)
21
-
22
- def test_generate_new_upload_url(self):
23
- settlement = self.create_new_settlement()
24
- new_settlement = Settlement(id=settlement.settlement_id, file_name='updated_settlement_sample.csv')
25
- result = Settlement(**new_settlement.generate_new_upload_url())
26
- self.assertIsNotNone(result.settlement_id)
27
- self.assertIsNotNone(result.upload_url)
28
-
29
- def test_upload_file(self):
30
- settlement, response = self.upload_settlement_file('settlement_sample.csv')
31
- self.assertEqual(200, response.status_code)
32
-
33
- def test_get_validaitons(self):
34
- settlement, response = self.upload_settlement_file('settlement_sample_bad.csv')
35
- validations = SettlementValidation.get(settlement.settlement_id, page=1, per_page=10)
36
- self.assertIsNotNone(validations)
37
-
38
- def test_cancel_settlement(self):
39
- settlement, response = self.upload_settlement_file('settlement_sample_bad.csv')
40
- cancelled = Settlement.cancel(settlement.settlement_id)
41
- self.assertIsNotNone(cancelled)
42
- self.assertEqual("CANCELLED", cancelled['status'])
43
-
44
- @staticmethod
45
- def create_new_settlement():
46
- if SettlementTestLive._settlement is None:
47
- settlement = Settlement(file_name='settlement_sample.csv')
48
- SettlementTestLive._settlement = Settlement(**settlement.generate_upload_url())
49
- return SettlementTestLive._settlement
50
-
51
- def upload_settlement_file(self, file_name):
52
- settlement = Settlement(file_name=file_name)
53
- settlement = Settlement(**settlement.generate_upload_url())
54
- file_path = os.path.join(os.path.dirname(__file__), 'resources', file_name)
55
- with open(file_path, 'rb') as f:
56
- data = f.read()
57
-
58
- headers = {'Content-Type': 'text/csv'}
59
- response = requests.put(settlement.upload_url, data=data, headers=headers)
60
- return settlement, response