first commit
This commit is contained in:
2
modules/account_payment_stripe/tests/__init__.py
Normal file
2
modules/account_payment_stripe/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,377 @@
|
||||
===============================
|
||||
Account Payment Stripe Scenario
|
||||
===============================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> import os
|
||||
>>> import time
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> import stripe
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import create_chart, create_fiscalyear
|
||||
>>> from trytond.modules.company.tests.tools import create_company, get_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
>>> FETCH_SLEEP, MAX_SLEEP = 1, 100
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules(
|
||||
... 'account_payment_stripe', create_company, create_chart)
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = create_fiscalyear(today=today)
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Create Stripe account::
|
||||
|
||||
>>> StripeAccount = Model.get('account.payment.stripe.account')
|
||||
>>> stripe_account = StripeAccount(name="Stripe")
|
||||
>>> stripe_account.secret_key = os.getenv('STRIPE_SECRET_KEY')
|
||||
>>> stripe_account.publishable_key = os.getenv('STRIPE_PUBLISHABLE_KEY')
|
||||
>>> stripe_account.save()
|
||||
>>> stripe.api_key = os.getenv('STRIPE_SECRET_KEY')
|
||||
|
||||
Create webhook identifier::
|
||||
|
||||
>>> stripe_account.click('new_identifier')
|
||||
>>> len(stripe_account.webhook_identifier)
|
||||
32
|
||||
|
||||
Remove webhook::
|
||||
|
||||
>>> stripe_account.click('new_identifier')
|
||||
>>> stripe_account.webhook_identifier
|
||||
|
||||
Setup fetch events cron::
|
||||
|
||||
>>> Cron = Model.get('ir.cron')
|
||||
>>> cron_fetch_events, = Cron.find([
|
||||
... ('method', '=', 'account.payment.stripe.account|fetch_events'),
|
||||
... ])
|
||||
>>> cron_fetch_events.companies.append(get_company())
|
||||
|
||||
Create payment journal::
|
||||
|
||||
>>> PaymentJournal = Model.get('account.payment.journal')
|
||||
>>> payment_journal = PaymentJournal(name="Stripe",
|
||||
... process_method='stripe', stripe_account=stripe_account)
|
||||
>>> payment_journal.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> Lang = Model.get('ir.lang')
|
||||
>>> customer = Party(name='Customer')
|
||||
>>> customer.lang, = Lang.find([('code', '=', 'en')])
|
||||
>>> customer.save()
|
||||
|
||||
Create submitted payment::
|
||||
|
||||
>>> Payment = Model.get('account.payment')
|
||||
>>> payment = Payment()
|
||||
>>> payment.journal = payment_journal
|
||||
>>> payment.kind = 'receivable'
|
||||
>>> payment.party = customer
|
||||
>>> payment.amount = Decimal('42')
|
||||
>>> payment.reference = 'Testing'
|
||||
>>> payment.click('submit')
|
||||
>>> payment.state
|
||||
'submitted'
|
||||
|
||||
Checkout the payment::
|
||||
|
||||
>>> checkout = payment.click('stripe_checkout')
|
||||
>>> bool(payment.stripe_checkout_id)
|
||||
True
|
||||
|
||||
>>> token = stripe.Token.create(
|
||||
... card={
|
||||
... 'number': '4242424242424242',
|
||||
... 'exp_month': 12,
|
||||
... 'exp_year': today.year + 1,
|
||||
... 'cvc': '123',
|
||||
... },
|
||||
... )
|
||||
>>> Payment.write([payment.id], {
|
||||
... 'stripe_token': token.id,
|
||||
... 'stripe_chargeable': True,
|
||||
... 'stripe_payment_intent_id': None, # Remove intent from checkout
|
||||
... }, config.context)
|
||||
|
||||
Process the payment::
|
||||
|
||||
>>> process_payment = payment.click('process_wizard')
|
||||
>>> payment.state
|
||||
'processing'
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... payment.reload()
|
||||
... if payment.state == 'succeeded':
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> payment.state
|
||||
'succeeded'
|
||||
>>> bool(payment.stripe_captured)
|
||||
True
|
||||
|
||||
Create failing payment::
|
||||
|
||||
>>> previous_idempotency_key = payment.stripe_idempotency_key
|
||||
>>> payment, = payment.duplicate()
|
||||
>>> payment.stripe_idempotency_key != previous_idempotency_key
|
||||
True
|
||||
>>> payment.click('submit')
|
||||
>>> payment.state
|
||||
'submitted'
|
||||
>>> checkout = payment.click('stripe_checkout')
|
||||
>>> bool(payment.stripe_checkout_id)
|
||||
True
|
||||
>>> token = stripe.Token.create(
|
||||
... card={
|
||||
... 'number': '4000000000000002',
|
||||
... 'exp_month': 12,
|
||||
... 'exp_year': today.year + 1,
|
||||
... 'cvc': '123',
|
||||
... },
|
||||
... )
|
||||
>>> Payment.write([payment.id], {
|
||||
... 'stripe_token': token.id,
|
||||
... 'stripe_chargeable': True,
|
||||
... 'stripe_payment_intent_id': None, # Remove intent from checkout
|
||||
... }, config.context)
|
||||
>>> process_payment = payment.click('process_wizard')
|
||||
>>> payment.state
|
||||
'failed'
|
||||
>>> payment.stripe_error_code
|
||||
'card_declined'
|
||||
|
||||
Create a customer::
|
||||
|
||||
>>> Customer = Model.get('account.payment.stripe.customer')
|
||||
>>> stripe_customer = Customer()
|
||||
>>> stripe_customer.party = customer
|
||||
>>> stripe_customer.stripe_account = stripe_account
|
||||
|
||||
Checkout the customer::
|
||||
|
||||
>>> checkout = stripe_customer.click('stripe_checkout')
|
||||
>>> bool(stripe_customer.stripe_checkout_id)
|
||||
True
|
||||
|
||||
>>> token = stripe.Token.create(
|
||||
... card={
|
||||
... 'number': '4012888888881881',
|
||||
... 'exp_month': 12,
|
||||
... 'exp_year': today.year + 1,
|
||||
... 'cvc': '123',
|
||||
... },
|
||||
... )
|
||||
>>> Customer.write(
|
||||
... [stripe_customer.id], {'stripe_token': token.id}, config.context)
|
||||
|
||||
Run cron::
|
||||
|
||||
>>> cron_customer_create, = Cron.find([
|
||||
... ('method', '=', 'account.payment.stripe.customer|stripe_create'),
|
||||
... ])
|
||||
>>> cron_customer_create.companies.append(get_company())
|
||||
>>> cron_customer_create.click('run_once')
|
||||
|
||||
>>> stripe_customer.reload()
|
||||
>>> bool(stripe_customer.stripe_customer_id)
|
||||
True
|
||||
|
||||
Update customer::
|
||||
|
||||
>>> contact = customer.contact_mechanisms.new()
|
||||
>>> contact.type = 'email'
|
||||
>>> contact.value = 'customer@example.com'
|
||||
>>> customer.save()
|
||||
|
||||
>>> cus = stripe.Customer.retrieve(stripe_customer.stripe_customer_id)
|
||||
>>> cus.email
|
||||
'customer@example.com'
|
||||
>>> cus.preferred_locales
|
||||
['en']
|
||||
|
||||
Make payment with customer::
|
||||
|
||||
>>> payment, = payment.duplicate()
|
||||
>>> payment.stripe_customer = stripe_customer
|
||||
>>> payment.save()
|
||||
>>> _, source = Payment.get_stripe_customer_sources(payment.id, config.context)
|
||||
>>> source_id, source_name = source
|
||||
>>> source_name
|
||||
'Visa ****1881 12/...'
|
||||
>>> payment.stripe_customer_source = source_id
|
||||
>>> payment.click('submit')
|
||||
>>> payment.state
|
||||
'submitted'
|
||||
>>> process_payment = payment.click('process_wizard')
|
||||
>>> payment.state
|
||||
'processing'
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... payment.reload()
|
||||
... if payment.state == 'succeeded':
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> payment.state
|
||||
'succeeded'
|
||||
|
||||
Detach source::
|
||||
|
||||
>>> detach = stripe_customer.click('detach_source')
|
||||
>>> detach.form.source = source_id
|
||||
>>> detach.execute('detach')
|
||||
|
||||
>>> cus = stripe.Customer.retrieve(
|
||||
... stripe_customer.stripe_customer_id, expand=['sources'])
|
||||
>>> len(cus.sources)
|
||||
0
|
||||
>>> len(stripe.PaymentMethod.list(customer=cus.id, type='card'))
|
||||
0
|
||||
|
||||
Delete customer::
|
||||
|
||||
>>> stripe_customer.delete()
|
||||
>>> bool(stripe_customer.active)
|
||||
False
|
||||
|
||||
Run cron::
|
||||
|
||||
>>> cron_customer_delete, = Cron.find([
|
||||
... ('method', '=', 'account.payment.stripe.customer|stripe_delete'),
|
||||
... ])
|
||||
>>> cron_customer_delete.companies.append(get_company())
|
||||
>>> cron_customer_delete.click('run_once')
|
||||
|
||||
>>> stripe_customer.reload()
|
||||
>>> stripe_customer.stripe_token
|
||||
>>> stripe_customer.stripe_customer_id
|
||||
|
||||
Create capture payment::
|
||||
|
||||
>>> payment, = payment.duplicate()
|
||||
>>> payment.stripe_capture = False
|
||||
>>> payment.click('submit')
|
||||
>>> payment.state
|
||||
'submitted'
|
||||
|
||||
Checkout the capture payment::
|
||||
|
||||
>>> token = stripe.Token.create(
|
||||
... card={
|
||||
... 'number': '4242424242424242',
|
||||
... 'exp_month': 12,
|
||||
... 'exp_year': today.year + 1,
|
||||
... 'cvc': '123',
|
||||
... },
|
||||
... )
|
||||
>>> Payment.write([payment.id], {
|
||||
... 'stripe_token': token.id,
|
||||
... }, config.context)
|
||||
|
||||
Process the capture payment::
|
||||
|
||||
>>> process_payment = payment.click('process_wizard')
|
||||
>>> payment.state
|
||||
'processing'
|
||||
>>> bool(payment.stripe_captured)
|
||||
False
|
||||
|
||||
Capture lower amount::
|
||||
|
||||
>>> payment.amount = Decimal('40')
|
||||
>>> payment.click('stripe_do_capture')
|
||||
>>> payment.state
|
||||
'processing'
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... payment.reload()
|
||||
... if payment.state == 'succeeded':
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> payment.state
|
||||
'succeeded'
|
||||
>>> bool(payment.stripe_captured)
|
||||
True
|
||||
|
||||
Refund some amount::
|
||||
|
||||
>>> Refund = Model.get('account.payment.stripe.refund')
|
||||
>>> refund = Refund()
|
||||
>>> refund.payment = payment
|
||||
>>> refund.amount = Decimal('38')
|
||||
>>> refund.click('submit')
|
||||
>>> refund.click('approve')
|
||||
>>> cron_refund_create, = Cron.find([
|
||||
... ('method', '=', 'account.payment.stripe.refund|stripe_create'),
|
||||
... ])
|
||||
>>> cron_refund_create.click('run_once')
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... payment.reload()
|
||||
... if payment.amount == Decimal('2.00'):
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> payment.amount
|
||||
Decimal('2.00')
|
||||
>>> payment.state
|
||||
'succeeded'
|
||||
>>> refund.reload()
|
||||
>>> refund.state
|
||||
'succeeded'
|
||||
|
||||
Simulate charge.refunded event with full amount::
|
||||
|
||||
>>> refund = Refund()
|
||||
>>> refund.payment = payment
|
||||
>>> refund.amount = Decimal('2')
|
||||
>>> refund.click('submit')
|
||||
>>> refund.click('approve')
|
||||
>>> cron_refund_create.click('run_once')
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... payment.reload()
|
||||
... if payment.amount == Decimal('0.00'):
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> payment.amount
|
||||
Decimal('0.00')
|
||||
>>> payment.state
|
||||
'failed'
|
||||
>>> refund.reload()
|
||||
>>> refund.state
|
||||
'succeeded'
|
||||
|
||||
Try to refund more::
|
||||
|
||||
>>> refund = Refund()
|
||||
>>> refund.payment = payment
|
||||
>>> refund.amount = Decimal('10')
|
||||
>>> refund.click('submit')
|
||||
>>> refund.click('approve')
|
||||
>>> cron_refund_create.click('run_once')
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... refund.reload()
|
||||
... if refund.state == 'failed':
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> refund.state
|
||||
'failed'
|
||||
@@ -0,0 +1,294 @@
|
||||
=======================================
|
||||
Account Payment Stripe Dispute Scenario
|
||||
=======================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> import os
|
||||
>>> import time
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> import stripe
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import create_chart, create_fiscalyear
|
||||
>>> from trytond.modules.company.tests.tools import create_company, get_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
>>> FETCH_SLEEP, MAX_SLEEP = 1, 100
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules(
|
||||
... 'account_payment_stripe', create_company, create_chart)
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = create_fiscalyear(today=today)
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Create Stripe account::
|
||||
|
||||
>>> StripeAccount = Model.get('account.payment.stripe.account')
|
||||
>>> stripe_account = StripeAccount(name="Stripe")
|
||||
>>> stripe_account.secret_key = os.getenv('STRIPE_SECRET_KEY')
|
||||
>>> stripe_account.publishable_key = os.getenv('STRIPE_PUBLISHABLE_KEY')
|
||||
>>> stripe_account.save()
|
||||
>>> stripe.api_key = os.getenv('STRIPE_SECRET_KEY')
|
||||
|
||||
Setup fetch events cron::
|
||||
|
||||
>>> Cron = Model.get('ir.cron')
|
||||
>>> cron_fetch_events, = Cron.find([
|
||||
... ('method', '=', 'account.payment.stripe.account|fetch_events'),
|
||||
... ])
|
||||
>>> cron_fetch_events.companies.append(get_company())
|
||||
|
||||
Create payment journal::
|
||||
|
||||
>>> PaymentJournal = Model.get('account.payment.journal')
|
||||
>>> payment_journal = PaymentJournal(name="Stripe",
|
||||
... process_method='stripe', stripe_account=stripe_account)
|
||||
>>> payment_journal.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> customer = Party(name='Customer')
|
||||
>>> customer.save()
|
||||
|
||||
Create fully disputed payment::
|
||||
|
||||
>>> Payment = Model.get('account.payment')
|
||||
>>> payment = Payment()
|
||||
>>> payment.journal = payment_journal
|
||||
>>> payment.kind = 'receivable'
|
||||
>>> payment.party = customer
|
||||
>>> payment.amount = Decimal('42')
|
||||
>>> payment.reference = 'Testing'
|
||||
>>> payment.click('submit')
|
||||
>>> payment.state
|
||||
'submitted'
|
||||
|
||||
>>> checkout = payment.click('stripe_checkout')
|
||||
>>> bool(payment.stripe_checkout_id)
|
||||
True
|
||||
|
||||
>>> token = stripe.Token.create(
|
||||
... card={
|
||||
... 'number': '4000000000000259',
|
||||
... 'exp_month': 12,
|
||||
... 'exp_year': today.year + 1,
|
||||
... 'cvc': '123',
|
||||
... },
|
||||
... )
|
||||
>>> Payment.write([payment.id], {
|
||||
... 'stripe_token': token.id,
|
||||
... 'stripe_chargeable': True,
|
||||
... 'stripe_payment_intent_id': None, # Remove intent from checkout
|
||||
... }, config.context)
|
||||
|
||||
>>> process_payment = payment.click('process_wizard')
|
||||
>>> payment.state
|
||||
'processing'
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... payment.reload()
|
||||
... if payment.state == 'succeeded':
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> payment.state
|
||||
'succeeded'
|
||||
>>> bool(payment.stripe_captured)
|
||||
True
|
||||
|
||||
Simulate charge.dispute.created event::
|
||||
|
||||
>>> StripeAccount.webhook([stripe_account], {
|
||||
... 'type': 'charge.dispute.created',
|
||||
... 'data': {
|
||||
... 'object': {
|
||||
... 'object': 'dispute',
|
||||
... 'charge': payment.stripe_charge_id,
|
||||
... 'amount': 4200,
|
||||
... 'currency': 'usd',
|
||||
... 'reason': 'customer_initiated',
|
||||
... 'status': 'needs_response',
|
||||
... },
|
||||
... },
|
||||
... }, {})
|
||||
[True]
|
||||
>>> payment.reload()
|
||||
>>> payment.state
|
||||
'succeeded'
|
||||
>>> payment.stripe_dispute_reason
|
||||
'customer_initiated'
|
||||
>>> payment.stripe_dispute_status
|
||||
'needs_response'
|
||||
|
||||
Simulate charge.dispute.closed event::
|
||||
|
||||
>>> StripeAccount.webhook([stripe_account], {
|
||||
... 'type': 'charge.dispute.closed',
|
||||
... 'data': {
|
||||
... 'object': {
|
||||
... 'object': 'dispute',
|
||||
... 'charge': payment.stripe_charge_id,
|
||||
... 'amount': 4200,
|
||||
... 'currency': 'usd',
|
||||
... 'reason': 'customer_initiated',
|
||||
... 'status': 'lost',
|
||||
... },
|
||||
... },
|
||||
... }, {})
|
||||
[True]
|
||||
>>> payment.reload()
|
||||
>>> payment.state
|
||||
'failed'
|
||||
>>> payment.stripe_dispute_reason
|
||||
'customer_initiated'
|
||||
>>> payment.stripe_dispute_status
|
||||
'lost'
|
||||
|
||||
Create partial disputed payment::
|
||||
|
||||
>>> Payment = Model.get('account.payment')
|
||||
>>> payment = Payment()
|
||||
>>> payment.journal = payment_journal
|
||||
>>> payment.kind = 'receivable'
|
||||
>>> payment.party = customer
|
||||
>>> payment.amount = Decimal('42')
|
||||
>>> payment.reference = 'Testing'
|
||||
>>> payment.click('submit')
|
||||
>>> payment.state
|
||||
'submitted'
|
||||
|
||||
>>> checkout = payment.click('stripe_checkout')
|
||||
>>> bool(payment.stripe_checkout_id)
|
||||
True
|
||||
|
||||
>>> token = stripe.Token.create(
|
||||
... card={
|
||||
... 'number': '4000000000000259',
|
||||
... 'exp_month': 12,
|
||||
... 'exp_year': today.year + 1,
|
||||
... 'cvc': '123',
|
||||
... },
|
||||
... )
|
||||
>>> Payment.write([payment.id], {
|
||||
... 'stripe_token': token.id,
|
||||
... 'stripe_chargeable': True,
|
||||
... 'stripe_payment_intent_id': None, # Remove intent from checkout
|
||||
... }, config.context)
|
||||
|
||||
>>> process_payment = payment.click('process_wizard')
|
||||
>>> payment.state
|
||||
'processing'
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... payment.reload()
|
||||
... if payment.state == 'succeeded':
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> payment.state
|
||||
'succeeded'
|
||||
>>> bool(payment.stripe_captured)
|
||||
True
|
||||
|
||||
Simulate charge.dispute.closed event::
|
||||
|
||||
>>> StripeAccount.webhook([stripe_account], {
|
||||
... 'type': 'charge.dispute.closed',
|
||||
... 'data': {
|
||||
... 'object': {
|
||||
... 'object': 'dispute',
|
||||
... 'charge': payment.stripe_charge_id,
|
||||
... 'amount': 1200,
|
||||
... 'currency': 'usd',
|
||||
... 'reason': 'general',
|
||||
... 'status': 'lost',
|
||||
... },
|
||||
... },
|
||||
... }, {})
|
||||
[True]
|
||||
>>> payment.reload()
|
||||
>>> payment.state
|
||||
'succeeded'
|
||||
>>> payment.amount
|
||||
Decimal('30.00')
|
||||
>>> payment.stripe_dispute_reason
|
||||
'general'
|
||||
>>> payment.stripe_dispute_status
|
||||
'lost'
|
||||
|
||||
Create won disputed payment::
|
||||
|
||||
>>> Payment = Model.get('account.payment')
|
||||
>>> payment = Payment()
|
||||
>>> payment.journal = payment_journal
|
||||
>>> payment.kind = 'receivable'
|
||||
>>> payment.party = customer
|
||||
>>> payment.amount = Decimal('42')
|
||||
>>> payment.reference = 'Testing'
|
||||
>>> payment.click('submit')
|
||||
>>> payment.state
|
||||
'submitted'
|
||||
|
||||
>>> checkout = payment.click('stripe_checkout')
|
||||
>>> bool(payment.stripe_checkout_id)
|
||||
True
|
||||
|
||||
>>> token = stripe.Token.create(
|
||||
... card={
|
||||
... 'number': '4000000000000259',
|
||||
... 'exp_month': 12,
|
||||
... 'exp_year': today.year + 1,
|
||||
... 'cvc': '123',
|
||||
... },
|
||||
... )
|
||||
>>> Payment.write([payment.id], {
|
||||
... 'stripe_token': token.id,
|
||||
... 'stripe_chargeable': True,
|
||||
... 'stripe_payment_intent_id': None, # Remove intent from checkout
|
||||
... }, config.context)
|
||||
|
||||
>>> process_payment = payment.click('process_wizard')
|
||||
>>> payment.state
|
||||
'processing'
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... payment.reload()
|
||||
... if payment.state == 'succeeded':
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> payment.state
|
||||
'succeeded'
|
||||
>>> bool(payment.stripe_captured)
|
||||
True
|
||||
|
||||
Simulate charge.dispute.closed event::
|
||||
|
||||
>>> charge = stripe.Charge.retrieve(payment.stripe_charge_id)
|
||||
>>> dispute = stripe.Dispute.modify(charge.dispute,
|
||||
... evidence={'uncategorized_text': 'winning_evidence'})
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... payment.reload()
|
||||
... if payment.stripe_dispute_status == 'won':
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> payment.state
|
||||
'succeeded'
|
||||
>>> payment.amount
|
||||
Decimal('42.00')
|
||||
>>> payment.stripe_dispute_reason
|
||||
'fraudulent'
|
||||
>>> payment.stripe_dispute_status
|
||||
'won'
|
||||
@@ -0,0 +1,105 @@
|
||||
================================
|
||||
Account Payment Stripe Identical
|
||||
================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> import os
|
||||
|
||||
>>> import stripe
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.company.tests.tools import create_company, get_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_payment_stripe', create_company)
|
||||
|
||||
>>> Company = Model.get('company.company')
|
||||
>>> Cron = Model.get('ir.cron')
|
||||
>>> StripeAccount = Model.get('account.payment.stripe.account')
|
||||
>>> StripeCustomer = Model.get('account.payment.stripe.customer')
|
||||
|
||||
Get company::
|
||||
|
||||
>>> company = get_company()
|
||||
|
||||
Create Stripe account::
|
||||
|
||||
>>> stripe_account = StripeAccount(name="Stripe")
|
||||
>>> stripe_account.secret_key = os.getenv('STRIPE_SECRET_KEY')
|
||||
>>> stripe_account.publishable_key = os.getenv('STRIPE_PUBLISHABLE_KEY')
|
||||
>>> stripe_account.save()
|
||||
>>> stripe.api_key = os.getenv('STRIPE_SECRET_KEY')
|
||||
|
||||
Setup cron::
|
||||
|
||||
>>> cron_customer_create, = Cron.find([
|
||||
... ('method', '=', 'account.payment.stripe.customer|stripe_create'),
|
||||
... ])
|
||||
>>> cron_customer_create.companies.append(Company(company.id))
|
||||
>>> cron_customer_create.save()
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> customer1 = Party(name="Customer 1")
|
||||
>>> customer1.save()
|
||||
|
||||
>>> customer2 = Party(name="Customer 2")
|
||||
>>> customer2.save()
|
||||
|
||||
Create a customer::
|
||||
|
||||
>>> stripe_customer1 = StripeCustomer()
|
||||
>>> stripe_customer1.party = customer1
|
||||
>>> stripe_customer1.stripe_account = stripe_account
|
||||
>>> _ = stripe_customer1.click('stripe_checkout')
|
||||
>>> token = stripe.Token.create(
|
||||
... card={
|
||||
... 'number': '4012888888881881',
|
||||
... 'exp_month': 12,
|
||||
... 'exp_year': today.year + 1,
|
||||
... 'cvc': '123',
|
||||
... },
|
||||
... )
|
||||
>>> StripeCustomer.write(
|
||||
... [stripe_customer1.id], {'stripe_token': token.id}, config.context)
|
||||
|
||||
Run cron::
|
||||
|
||||
>>> cron_customer_create.click('run_once')
|
||||
|
||||
>>> stripe_customer1.reload()
|
||||
>>> stripe_customer1.identical_customers
|
||||
[]
|
||||
|
||||
Create a second customer with same card::
|
||||
|
||||
>>> stripe_customer2 = StripeCustomer()
|
||||
>>> stripe_customer2.party = customer2
|
||||
>>> stripe_customer2.stripe_account = stripe_account
|
||||
>>> _ = stripe_customer2.click('stripe_checkout')
|
||||
>>> token = stripe.Token.create(
|
||||
... card={
|
||||
... 'number': '4012888888881881',
|
||||
... 'exp_month': 12,
|
||||
... 'exp_year': today.year + 1,
|
||||
... 'cvc': '123',
|
||||
... },
|
||||
... )
|
||||
>>> StripeCustomer.write(
|
||||
... [stripe_customer2.id], {'stripe_token': token.id}, config.context)
|
||||
|
||||
Run cron::
|
||||
|
||||
>>> cron_customer_create.click('run_once')
|
||||
|
||||
>>> stripe_customer2.reload()
|
||||
>>> assertEqual(stripe_customer2.identical_customers, [stripe_customer1])
|
||||
>>> stripe_customer1.reload()
|
||||
>>> assertEqual(stripe_customer1.identical_customers, [stripe_customer2])
|
||||
@@ -0,0 +1,173 @@
|
||||
======================================
|
||||
Account Payment Stripe Intent Scenario
|
||||
======================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> import os
|
||||
>>> import time
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> import stripe
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import create_chart, create_fiscalyear
|
||||
>>> from trytond.modules.company.tests.tools import create_company, get_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
>>> FETCH_SLEEP, MAX_SLEEP = 1, 100
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules(
|
||||
... 'account_payment_stripe', create_company, create_chart)
|
||||
|
||||
Get company::
|
||||
|
||||
>>> Company = Model.get('company.company')
|
||||
>>> company = get_company()
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = create_fiscalyear(today=today)
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Create Stripe account::
|
||||
|
||||
>>> StripeAccount = Model.get('account.payment.stripe.account')
|
||||
>>> stripe_account = StripeAccount(name="Stripe")
|
||||
>>> stripe_account.secret_key = os.getenv('STRIPE_SECRET_KEY')
|
||||
>>> stripe_account.publishable_key = os.getenv('STRIPE_PUBLISHABLE_KEY')
|
||||
>>> stripe_account.save()
|
||||
>>> stripe.api_key = os.getenv('STRIPE_SECRET_KEY')
|
||||
|
||||
Setup fetch events cron::
|
||||
|
||||
>>> Cron = Model.get('ir.cron')
|
||||
>>> cron_fetch_events, = Cron.find([
|
||||
... ('method', '=', 'account.payment.stripe.account|fetch_events'),
|
||||
... ])
|
||||
>>> cron_fetch_events.companies.append(Company(company.id))
|
||||
|
||||
Create payment journal::
|
||||
|
||||
>>> PaymentJournal = Model.get('account.payment.journal')
|
||||
>>> payment_journal = PaymentJournal(name="Stripe",
|
||||
... process_method='stripe', stripe_account=stripe_account)
|
||||
>>> payment_journal.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party(name='Customer')
|
||||
>>> party.save()
|
||||
|
||||
Register card::
|
||||
|
||||
>>> Cron = Model.get('ir.cron')
|
||||
>>> Customer = Model.get('account.payment.stripe.customer')
|
||||
>>> customer = Customer()
|
||||
>>> customer.party = party
|
||||
>>> customer.stripe_account = stripe_account
|
||||
>>> customer.save()
|
||||
|
||||
>>> _ = customer.click('stripe_checkout')
|
||||
>>> setup_intent = stripe.SetupIntent.confirm(
|
||||
... customer.stripe_setup_intent_id,
|
||||
... return_url='http://localhost/',
|
||||
... payment_method='pm_card_visa')
|
||||
>>> cron_update_intent, = Cron.find([
|
||||
... ('method', '=', 'account.payment.stripe.customer|stripe_intent_update'),
|
||||
... ])
|
||||
>>> cron_update_intent.companies.append(Company(company.id))
|
||||
>>> cron_update_intent.click('run_once')
|
||||
>>> customer.reload()
|
||||
>>> bool(customer.stripe_customer_id)
|
||||
True
|
||||
|
||||
Create submitted payment::
|
||||
|
||||
>>> Payment = Model.get('account.payment')
|
||||
>>> payment = Payment()
|
||||
>>> payment.journal = payment_journal
|
||||
>>> payment.kind = 'receivable'
|
||||
>>> payment.party = party
|
||||
>>> payment.amount = Decimal('42')
|
||||
>>> payment.reference = 'Testing'
|
||||
>>> payment.stripe_customer = customer
|
||||
>>> payment.stripe_customer_payment_method = 'pm_card_visa'
|
||||
>>> payment.click('submit')
|
||||
>>> payment.state
|
||||
'submitted'
|
||||
|
||||
Process off-session the payment::
|
||||
|
||||
>>> process_payment = payment.click('process_wizard')
|
||||
>>> payment.state
|
||||
'processing'
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... payment.reload()
|
||||
... if payment.state == 'succeeded':
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> payment.state
|
||||
'succeeded'
|
||||
>>> bool(payment.stripe_captured)
|
||||
True
|
||||
|
||||
Refund the payment::
|
||||
|
||||
>>> Refund = Model.get('account.payment.stripe.refund')
|
||||
>>> refund = Refund()
|
||||
>>> refund.payment = payment
|
||||
>>> refund.amount = payment.amount
|
||||
>>> refund.click('submit')
|
||||
>>> refund.click('approve')
|
||||
>>> cron_refund_create, = Cron.find([
|
||||
... ('method', '=', 'account.payment.stripe.refund|stripe_create'),
|
||||
... ])
|
||||
>>> cron_refund_create.click('run_once')
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... payment.reload()
|
||||
... if payment.state == 'failed':
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> payment.state
|
||||
'failed'
|
||||
|
||||
Cancel payment intent::
|
||||
|
||||
>>> payment = Payment()
|
||||
>>> payment.journal = payment_journal
|
||||
>>> payment.kind = 'receivable'
|
||||
>>> payment.party = party
|
||||
>>> payment.amount = Decimal('42')
|
||||
>>> payment.reference = 'Testing canceled'
|
||||
>>> payment.stripe_customer = customer
|
||||
>>> payment.stripe_customer_payment_method = 'pm_card_visa'
|
||||
>>> payment.stripe_capture = False
|
||||
>>> payment.click('submit')
|
||||
>>> payment.state
|
||||
'submitted'
|
||||
|
||||
>>> process_payment = payment.click('process_wizard')
|
||||
>>> payment.state
|
||||
'processing'
|
||||
|
||||
>>> _ = stripe.PaymentIntent.cancel(payment.stripe_payment_intent_id)
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... payment.reload()
|
||||
... if payment.state == 'failed':
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> payment.state
|
||||
'failed'
|
||||
@@ -0,0 +1,139 @@
|
||||
=====================================
|
||||
Account Payment Stripe Refund Failure
|
||||
=====================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> import os
|
||||
>>> import time
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> import stripe
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import create_chart, create_fiscalyear
|
||||
>>> from trytond.modules.company.tests.tools import create_company, get_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
>>> FETCH_SLEEP, MAX_SLEEP = 1, 100
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules(
|
||||
... 'account_payment_stripe', create_company, create_chart)
|
||||
|
||||
>>> Cron = Model.get('ir.cron')
|
||||
>>> Payment = Model.get('account.payment')
|
||||
>>> Refund = Model.get('account.payment.stripe.refund')
|
||||
>>> StripeAccount = Model.get('account.payment.stripe.account')
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = create_fiscalyear(today=today)
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Create Stripe account::
|
||||
|
||||
>>> stripe_account = StripeAccount(name="Stripe")
|
||||
>>> stripe_account.secret_key = os.getenv('STRIPE_SECRET_KEY')
|
||||
>>> stripe_account.publishable_key = os.getenv('STRIPE_PUBLISHABLE_KEY')
|
||||
>>> stripe_account.save()
|
||||
>>> stripe.api_key = os.getenv('STRIPE_SECRET_KEY')
|
||||
|
||||
Setup fetch events cron::
|
||||
|
||||
>>> cron_fetch_events, = Cron.find([
|
||||
... ('method', '=', 'account.payment.stripe.account|fetch_events'),
|
||||
... ])
|
||||
>>> cron_fetch_events.companies.append(get_company())
|
||||
|
||||
Create payment journal::
|
||||
|
||||
>>> PaymentJournal = Model.get('account.payment.journal')
|
||||
>>> payment_journal = PaymentJournal(name="Stripe",
|
||||
... process_method='stripe', stripe_account=stripe_account)
|
||||
>>> payment_journal.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party(name="Customer")
|
||||
>>> party.save()
|
||||
|
||||
Submit a payment::
|
||||
|
||||
>>> payment = Payment()
|
||||
>>> payment.journal = payment_journal
|
||||
>>> payment.kind = 'receivable'
|
||||
>>> payment.party = party
|
||||
>>> payment.amount = Decimal('42')
|
||||
>>> payment.reference = "Testing"
|
||||
>>> payment.click('submit')
|
||||
>>> payment.state
|
||||
'submitted'
|
||||
|
||||
Checkout the payment::
|
||||
|
||||
>>> token = stripe.Token.create(
|
||||
... card={
|
||||
... 'number': '4000000000005126', # async refund failure
|
||||
... 'exp_month': 12,
|
||||
... 'exp_year': today.year + 1,
|
||||
... 'cvc': '123',
|
||||
... },
|
||||
... )
|
||||
>>> Payment.write([payment.id], {
|
||||
... 'stripe_token': token.id,
|
||||
... 'stripe_chargeable': True,
|
||||
... }, config.context)
|
||||
|
||||
Process the payment::
|
||||
|
||||
>>> process_payment = payment.click('process_wizard')
|
||||
>>> payment.state
|
||||
'processing'
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... payment.reload()
|
||||
... if payment.state == 'succeeded':
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> payment.state
|
||||
'succeeded'
|
||||
|
||||
Refund some amount::
|
||||
|
||||
>>> refund = Refund()
|
||||
>>> refund.payment = payment
|
||||
>>> refund.amount = Decimal('12')
|
||||
>>> refund.click('submit')
|
||||
>>> refund.click('approve')
|
||||
>>> refund.state
|
||||
'approved'
|
||||
|
||||
>>> cron_refund_create, = Cron.find([
|
||||
... ('method', '=', 'account.payment.stripe.refund|stripe_create'),
|
||||
... ])
|
||||
>>> cron_refund_create.click('run_once')
|
||||
|
||||
>>> refund.reload()
|
||||
>>> refund.state
|
||||
'succeeded'
|
||||
|
||||
>>> for _ in range(MAX_SLEEP):
|
||||
... cron_fetch_events.click('run_once')
|
||||
... refund.reload()
|
||||
... if refund.state == 'failed':
|
||||
... break
|
||||
... time.sleep(FETCH_SLEEP)
|
||||
>>> refund.reload()
|
||||
>>> refund.state
|
||||
'failed'
|
||||
>>> payment.reload()
|
||||
>>> payment.amount
|
||||
Decimal('42.00')
|
||||
>>> payment.state
|
||||
'succeeded'
|
||||
13
modules/account_payment_stripe/tests/test_module.py
Normal file
13
modules/account_payment_stripe/tests/test_module.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.modules.party.tests import PartyCheckReplaceMixin
|
||||
from trytond.tests.test_tryton import ModuleTestCase
|
||||
|
||||
|
||||
class AccountPaymentStripeTestCase(PartyCheckReplaceMixin, ModuleTestCase):
|
||||
'Test Account Payment Stripe module'
|
||||
module = 'account_payment_stripe'
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
20
modules/account_payment_stripe/tests/test_scenario.py
Normal file
20
modules/account_payment_stripe/tests/test_scenario.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
import os
|
||||
|
||||
from trytond.tests.test_tryton import TEST_NETWORK, load_doc_tests
|
||||
|
||||
|
||||
def load_tests(*args, **kwargs):
|
||||
if (not TEST_NETWORK
|
||||
or not (os.getenv('STRIPE_SECRET_KEY')
|
||||
and os.getenv('STRIPE_PUBLISHABLE_KEY'))):
|
||||
kwargs.setdefault('skips', set()).update({
|
||||
'scenario_account_payment_stripe.rst',
|
||||
'scenario_account_payment_stripe_dispute.rst',
|
||||
'scenario_account_payment_stripe_identical.rst',
|
||||
'scenario_account_payment_stripe_intent.rst',
|
||||
'scenario_account_payment_stripe_refund_failure.rst',
|
||||
})
|
||||
return load_doc_tests(__name__, __file__, *args, **kwargs)
|
||||
Reference in New Issue
Block a user