Skip to content

Commit

Permalink
Merge pull request #340 from Mangopay/feature/spot-fx-endpoints
Browse files Browse the repository at this point in the history
Feature/Spot FX endpoints
  • Loading branch information
silvianagh authored Sep 29, 2023
2 parents 7f377fa + de6f6fe commit 48a29f2
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 17 deletions.
22 changes: 20 additions & 2 deletions mangopay/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
Reason, ReportTransactionsFilters, ReportWalletsFilters, \
PlatformCategorization, Billing, SecurityInfo, Birthplace, ApplepayPaymentData, GooglepayPaymentData, \
ScopeBlocked, BrowserInfo, Shipping, CurrentState, FallbackReason, InstantPayout, CountryAuthorizationData, \
PayinsLinked
PayinsLinked, ConversionRate


class FieldDescriptor(object):
Expand Down Expand Up @@ -858,7 +858,8 @@ def api_value(self, value):
class PayinsLinkedField(Field):
def python_value(self, value):
if value is not None:
return PayinsLinked(payin_capture_id=value['PayinCaptureId'], payin_complement_id=value['PayinComplementId'])
return PayinsLinked(payin_capture_id=value['PayinCaptureId'],
payin_complement_id=value['PayinComplementId'])
return value

def api_value(self, value):
Expand All @@ -871,3 +872,20 @@ def api_value(self, value):
}

return value

class ConversionRateField(Field):
def python_value(self, value):
if value is not None:
return ConversionRate(client_rate=value['ClientRate'], market_rate=value['MarketRate'])
return value

def api_value(self, value):
value = super(ConversionRateField, self).api_value(value)

if isinstance(value, ConversionRate):
value = {
'ClientRate': value.client_rate,
'MarketRate': value.market_rate
}

return value
60 changes: 59 additions & 1 deletion mangopay/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
ReportWalletsFiltersField, BillingField, SecurityInfoField, PlatformCategorizationField,
BirthplaceField, ApplepayPaymentDataField, GooglepayPaymentDataField, ScopeBlockedField,
BrowserInfoField, ShippingField, CurrentStateField, FallbackReasonField, InstantPayoutField,
CountryAuthorizationDataField, PayinsLinkedField)
CountryAuthorizationDataField, PayinsLinkedField, ConversionRateField)
from .query import InsertQuery, UpdateQuery, SelectQuery, ActionQuery


Expand Down Expand Up @@ -264,6 +264,64 @@ def get(cls, *args, **kwargs):
return ClientWallet.get(*tuple(args[0].split('_')), **kwargs)
return super(Wallet, cls).get(*args, **kwargs)

@python_2_unicode_compatible
class ConversionRate(BaseModel):
debited_currency = CharField(api_name='DebitedCurrency', required=True)
credited_currency = CharField(api_name='CreditedCurrency', required=True)
client_rate = CharField(api_name='ClientRate')
market_rate = CharField(api_name='MarketRate')

def get_conversion_rate(self, *args, **kwargs):
kwargs['debited_currency'] = self.debited_currency
kwargs['credited_currency'] = self.credited_currency
select = SelectQuery(ConversionRate, *args, **kwargs)
select.identifier = 'GET_CONVERSION_RATE'
return select.all(*args, **kwargs)

class Meta:
verbose_name = 'conversion_rate'
verbose_name_plural = 'conversion_rates'
url = {
'GET_CONVERSION_RATE' : '/conversion/rate/%(debited_currency)s/%(credited_currency)s'
}

@python_2_unicode_compatible
class InstantConversion(BaseModel):
author = ForeignKeyField(User, api_name='AuthorId', required=True)
debited_wallet = ForeignKeyField(Wallet, api_name='DebitedWalletId', required=True)
credited_wallet = ForeignKeyField(Wallet, api_name='CreditedWalletId', required=True)
debited_funds = MoneyField(api_name='DebitedFunds', required=True)
credited_funds = MoneyField(api_name='CreditedFunds', required=True)
conversion_rate = ConversionRateField(api_name='ConversionRate')
type = CharField(api_name='Type', choices=constants.TRANSACTION_TYPE_CHOICES, default=None)
nature = CharField(api_name='Nature', choices=constants.NATURE_CHOICES, default=None)
creation_date = DateTimeField(api_name='CreationDate')
result_code = CharField(api_name='ResultCode')
result_message = CharField(api_name='ResultMessage')
status = CharField(api_name='Status', choices=constants.STATUS_CHOICES, default=None)
execution_date = DateTimeField(api_name='ExecutionDate')

def create_instant_conversion(self, **kwargs):
insert = InsertQuery(self, **kwargs)
insert.insert_query = self.get_field_dict()
insert.identifier = 'CREATE_INSTANT_CONVERSION'
return insert.execute()

@staticmethod
def get_instant_conversion(id, *args, **kwargs):
kwargs['id'] = id
select = SelectQuery(InstantConversion, *args, **kwargs)
select.identifier = 'GET_INSTANT_CONVERSION'
return select.all(*args, **kwargs)

class Meta:
verbose_name = 'instant_conversion'
verbose_name_plural = 'instant_conversions'
url = {
'CREATE_INSTANT_CONVERSION' : '/instant-conversion',
'GET_INSTANT_CONVERSION': '/instant-conversion/%(id)s'
}


@python_2_unicode_compatible
class Transfer(BaseModel):
Expand Down
10 changes: 10 additions & 0 deletions mangopay/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -945,3 +945,13 @@ def to_api_json(self):
"TaxAmount": self.tax_amount,
"Description": self.description
}

@add_camelcase_aliases
class ConversionRate(object):
def __init__(self, client_rate=None, market_rate=None):
self.client_rate = client_rate
self.market_rate = market_rate

def __str__(self):
return 'Conversion rate: %s' % \
(self.client_rate, self.market_rate)
52 changes: 50 additions & 2 deletions tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
from mangopay import APIRequest
from mangopay import get_default_handler
from mangopay.auth import AuthorizationTokenManager, StaticStorageStrategy
from mangopay.constants import LEGAL_USER_TYPE_CHOICES
from mangopay.resources import BankAccount, Document, ReportTransactions, UboDeclaration, Ubo, Deposit
from mangopay.resources import BankAccount, Document, ReportTransactions, UboDeclaration, Ubo, Deposit, DirectPayIn
from mangopay.utils import Address, ReportTransactionsFilters, Birthplace, BrowserInfo
from tests import settings
from tests.mocks import RegisteredMocks
Expand Down Expand Up @@ -415,6 +414,12 @@ def get_johns_wallet(recreate=False):
BaseTestLive._johns_wallet = BaseTestLive.create_new_wallet()
return BaseTestLive._johns_wallet

@staticmethod
def get_johns_wallet_with_money(recreate=False):
if BaseTestLive._johns_wallet is None or recreate:
BaseTestLive._johns_wallet = BaseTestLive.create_new_wallet_with_money()
return BaseTestLive._johns_wallet

@staticmethod
def create_new_wallet():
wallet = Wallet()
Expand All @@ -423,6 +428,49 @@ def create_new_wallet():
wallet.description = 'WALLET IN EUR'
return Wallet(**wallet.save())

@staticmethod
def create_new_wallet_with_money():
user = BaseTestLive.get_john()

wallet = Wallet()
wallet.owners = (user,)
wallet.currency = 'EUR'
wallet.description = 'WALLET IN EUR'
wallet = Wallet(**wallet.save())

card_registration = CardRegistration()
card_registration.user = user
card_registration.currency = 'EUR'

saved_registration = card_registration.save()
data = {
'cardNumber': '4970107111111119',
'cardCvx': '123',
'cardExpirationDate': '1224',
'accessKeyRef': card_registration.access_key,
'data': card_registration.preregistration_data
}
headers = {
'content-type': 'application/x-www-form-urlencoded'
}
registration_data_response = requests.post(card_registration.card_registration_url, data=data, headers=headers)
saved_registration['registration_data'] = registration_data_response.text
updated_registration = CardRegistration(**saved_registration).save()
card_id = updated_registration['card_id']

direct_payin = DirectPayIn(author=user,
debited_funds=Money(amount=100, currency='EUR'),
fees=Money(amount=1, currency='EUR'),
credited_wallet_id=wallet,
card_id=card_id,
secure_mode="DEFAULT",
ip_address="2001:0620:0000:0000:0211:24FF:FE80:C12C",
browser_info=BaseTest.get_browser_info(),
secure_mode_return_url="https://www.ulule.com/")

direct_payin.save()
return direct_payin.credited_wallet

@staticmethod
def get_johns_transfer(recreate=False):
if BaseTestLive._johns_transfer is None or recreate:
Expand Down
81 changes: 81 additions & 0 deletions tests/test_instant_conversion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from mangopay.utils import Money
from tests.resources import ConversionRate, InstantConversion, Wallet
from tests.test_base import BaseTestLive


class InstantConversionTest(BaseTestLive):

def test_get_conversion_rate(self):
conversion_rate = ConversionRate()
conversion_rate.debited_currency = 'EUR'
conversion_rate.credited_currency = 'GBP'

complete_conversion_rate = conversion_rate.get_conversion_rate()

self.assertIsNotNone(complete_conversion_rate)
self.assertIsNotNone(complete_conversion_rate.data[0].client_rate)
self.assertIsNotNone(complete_conversion_rate.data[0].market_rate)

def test_create_instant_conversion(self):
user = BaseTestLive.get_john()

credited_wallet = Wallet()
credited_wallet.owners = (user,)
credited_wallet.currency = 'GBP'
credited_wallet.description = 'WALLET IN GBP'
credited_wallet = Wallet(**credited_wallet.save())

credited_funds = Money()
credited_funds.currency = 'GBP'

debited_funds = Money()
debited_funds.currency = 'EUR'
debited_funds.amount = 79

instant_conversion = InstantConversion()
instant_conversion.author = user
instant_conversion.credited_wallet = credited_wallet
instant_conversion.debited_wallet = BaseTestLive.create_new_wallet_with_money()
instant_conversion.credited_funds = credited_funds
instant_conversion.debited_funds = debited_funds
instant_conversion.tag = "instant conversion test"

instant_conversion_response = instant_conversion.create_instant_conversion()

self.assertIsNotNone(instant_conversion_response)
self.assertIsNotNone(instant_conversion_response['debited_funds'].amount)
self.assertIsNotNone(instant_conversion_response['credited_funds'].amount)
self.assertEqual(instant_conversion_response['status'], 'SUCCEEDED')

def test_get_instant_conversion(self):
user = BaseTestLive.get_john()

credited_wallet = Wallet()
credited_wallet.owners = (user,)
credited_wallet.currency = 'GBP'
credited_wallet.description = 'WALLET IN GBP'
credited_wallet = Wallet(**credited_wallet.save())

credited_funds = Money()
credited_funds.currency = 'GBP'

debited_funds = Money()
debited_funds.currency = 'EUR'
debited_funds.amount = 79

instant_conversion = InstantConversion()
instant_conversion.author = user
instant_conversion.credited_wallet = credited_wallet
instant_conversion.debited_wallet = BaseTestLive.create_new_wallet_with_money()
instant_conversion.credited_funds = credited_funds
instant_conversion.debited_funds = debited_funds
instant_conversion.tag = "instant conversion test"

instant_conversion_response = instant_conversion.create_instant_conversion()
returned_conversion_response = InstantConversion.get_instant_conversion(instant_conversion_response['id'])

self.assertIsNotNone(returned_conversion_response)
self.assertIsNotNone(returned_conversion_response.data[0])
self.assertIsNotNone(returned_conversion_response.data[0].debited_funds.amount)
self.assertIsNotNone(returned_conversion_response.data[0].credited_funds.amount)
self.assertEqual(returned_conversion_response.data[0].status, 'SUCCEEDED')
25 changes: 13 additions & 12 deletions tests/test_wallets.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from tests import settings
from tests.resources import NaturalUser, Wallet, Transfer

from tests.test_base import BaseTest

from datetime import date
Expand All @@ -17,7 +18,7 @@ def test_create_wallet(self):
self.register_mock([
{
'method': responses.POST,
'url': settings.MANGOPAY_API_SANDBOX_URL+settings.MANGOPAY_CLIENT_ID+'/wallets',
'url': settings.MANGOPAY_API_SANDBOX_URL + settings.MANGOPAY_CLIENT_ID + '/wallets',
'body': {
"Owners": [
"1169419"
Expand All @@ -36,7 +37,7 @@ def test_create_wallet(self):
},
{
'method': responses.GET,
'url': settings.MANGOPAY_API_SANDBOX_URL+settings.MANGOPAY_CLIENT_ID+'/wallets/1169421',
'url': settings.MANGOPAY_API_SANDBOX_URL + settings.MANGOPAY_CLIENT_ID + '/wallets/1169421',
'body': {
"Owners": [
"1169419"
Expand All @@ -55,7 +56,7 @@ def test_create_wallet(self):
},
{
'method': responses.PUT,
'url': settings.MANGOPAY_API_SANDBOX_URL+settings.MANGOPAY_CLIENT_ID+'/wallets/1169421',
'url': settings.MANGOPAY_API_SANDBOX_URL + settings.MANGOPAY_CLIENT_ID + '/wallets/1169421',
'body': {
"Owners": [
"1169419"
Expand All @@ -74,7 +75,7 @@ def test_create_wallet(self):
},
{
'method': responses.GET,
'url': settings.MANGOPAY_API_SANDBOX_URL+settings.MANGOPAY_CLIENT_ID+'/users/1169419/wallets',
'url': settings.MANGOPAY_API_SANDBOX_URL + settings.MANGOPAY_CLIENT_ID + '/users/1169419/wallets',
'body': [
{
"Owners": [
Expand Down Expand Up @@ -131,7 +132,7 @@ def test_related_wallet(self):
self.register_mock([
{
'method': responses.POST,
'url': settings.MANGOPAY_API_SANDBOX_URL+settings.MANGOPAY_CLIENT_ID+'/wallets',
'url': settings.MANGOPAY_API_SANDBOX_URL + settings.MANGOPAY_CLIENT_ID + '/wallets',
'body': {
"Owners": [
"1167492"
Expand All @@ -150,7 +151,7 @@ def test_related_wallet(self):
},
{
'method': responses.GET,
'url': settings.MANGOPAY_API_SANDBOX_URL+settings.MANGOPAY_CLIENT_ID+'/users/natural/1169419',
'url': settings.MANGOPAY_API_SANDBOX_URL + settings.MANGOPAY_CLIENT_ID + '/users/natural/1169419',
'body': {
"FirstName": "Victor",
"LastName": "Claver",
Expand All @@ -160,7 +161,7 @@ def test_related_wallet(self):
"City": "City",
"Region": "Region",
"PostalCode": "11222",
"Country": "FR"
"Country": "FR"
},
"Birthday": int(time.mktime(date.today().timetuple())),
"Nationality": "FR",
Expand All @@ -178,7 +179,7 @@ def test_related_wallet(self):
},
{
'method': responses.GET,
'url': settings.MANGOPAY_API_SANDBOX_URL+settings.MANGOPAY_CLIENT_ID+'/users/1169419/wallets',
'url': settings.MANGOPAY_API_SANDBOX_URL + settings.MANGOPAY_CLIENT_ID + '/users/1169419/wallets',
'body': [
{
"Owners": [
Expand Down Expand Up @@ -219,7 +220,7 @@ def test_retrieve_wallet_transactions(self):
self.register_mock([
{
'method': responses.POST,
'url': settings.MANGOPAY_API_SANDBOX_URL+settings.MANGOPAY_CLIENT_ID+'/wallets',
'url': settings.MANGOPAY_API_SANDBOX_URL + settings.MANGOPAY_CLIENT_ID + '/wallets',
'body': {
"Owners": [
"1167492"
Expand All @@ -238,7 +239,7 @@ def test_retrieve_wallet_transactions(self):
},
{
'method': responses.POST,
'url': settings.MANGOPAY_API_SANDBOX_URL+settings.MANGOPAY_CLIENT_ID+'/transfers',
'url': settings.MANGOPAY_API_SANDBOX_URL + settings.MANGOPAY_CLIENT_ID + '/transfers',
'body': {
"Id": "1169434",
"Tag": "custom tag",
Expand Down Expand Up @@ -270,7 +271,7 @@ def test_retrieve_wallet_transactions(self):
},
{
'method': responses.GET,
'url': settings.MANGOPAY_API_SANDBOX_URL+settings.MANGOPAY_CLIENT_ID+'/wallets/1169421/transactions',
'url': settings.MANGOPAY_API_SANDBOX_URL + settings.MANGOPAY_CLIENT_ID + '/wallets/1169421/transactions',
'body': [
{
"Id": "1169215",
Expand Down Expand Up @@ -326,4 +327,4 @@ def test_retrieve_wallet_transactions(self):
transactions = self.legal_user_wallet.transactions.all()

self.assertEqual(len(transactions), 1)
self.assertEqual(transactions[0].type, 'TRANSFER')
self.assertEqual(transactions[0].type, 'TRANSFER')

0 comments on commit 48a29f2

Please sign in to comment.