Skip to content

Commit

Permalink
Merge pull request #116 from tejpal-mirakl/tax-split-changes
Browse files Browse the repository at this point in the history
Tax split changes
  • Loading branch information
millin-stripe authored Feb 15, 2023
2 parents f9f3bc6 + efc5943 commit 748a983
Show file tree
Hide file tree
Showing 21 changed files with 382 additions and 78 deletions.
4 changes: 4 additions & 0 deletions .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,7 @@ MAILER_DSN=smtp://null
# we will wait at least that duration between two notification emails. Default to 10 minutes.
# 0 to disable throttling, can go as high as the notification worker max life, i.e. 3600 by default.
#MAIL_ON_NOTIFICATION_ENDPOINT_DOWN_COOLDOWN=10

# Tax split configuration for new product order & backlog product order, for new service order & backlog service order
#ENABLE_PAYMENT_TAX_SPLIT=false
#STRIPE_TAX_ACCOUNT=acct_xxxxxx
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ vendor
bin/.phpunit/
.env
.idea/*
.settings
.buildpath
.project
clover.xml
8 changes: 8 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ parameters:
env(ENABLE_SERVICE_PAYMENT_SPLIT): false
env(ENABLE_SERVICE_PAYMENT_REFUND): false
env(ENABLE_SELLER_ONBOARDING): true
env(ENABLE_PAYMENT_TAX_SPLIT): false
env(STRIPE_TAX_ACCOUNT): ""

default_redirect_onboarding: "%env(MIRAKL_HOST_NAME)%/mmp/shop/account/shop"
default_enable_product_payment_split: false
Expand All @@ -37,12 +39,14 @@ parameters:
app.stripe.webhook_seller_secret: "%env(default:deprecated_webhook_secret:STRIPE_SELLERS_WEBHOOK_SECRET)%"
app.stripe.webhook_operator_secret: "%env(default:deprecated_webhook_secret:STRIPE_OPERATOR_WEBHOOK_SECRET)%"
app.stripe.prefill_onboarding: "%env(bool:STRIPE_PREFILL_ONBOARDING)%"
app.stripe.tax_account: "%env(STRIPE_TAX_ACCOUNT)%"
app.workflow.enable_product_payment_split: "%env(default:deprecated_enable_product_payment_split:bool:ENABLE_PRODUCT_PAYMENT_SPLIT)%"
app.workflow.enable_service_payment_split: "%env(bool:ENABLE_SERVICE_PAYMENT_SPLIT)%"
app.workflow.enable_product_payment_refund: "%env(default:deprecated_enable_product_payment_refund:bool:ENABLE_PRODUCT_PAYMENT_REFUND)%"
app.workflow.enable_service_payment_refund: "%env(bool:ENABLE_SERVICE_PAYMENT_REFUND)%"
app.workflow.enable_seller_onboarding: "%env(bool:ENABLE_SELLER_ONBOARDING)%"
app.workflow.payment_metadata_commercial_order_id: "%env(default:deprecated_payment_metadata_commercial_order_id:PAYMENT_METADATA_COMMERCIAL_ORDER_ID)%"
app.workflow.enable_payment_tax_split: "%env(bool:ENABLE_PAYMENT_TAX_SPLIT)%"
app.mirakl.api_key: "%env(MIRAKL_API_KEY)%"
app.mirakl.host_name: "%env(MIRAKL_HOST_NAME)%"
app.mirakl.stripe_custom_field_code: "%env(MIRAKL_CUSTOM_FIELD_CODE)%"
Expand Down Expand Up @@ -84,6 +88,10 @@ services:
$technicalEmailFrom: "%app.mailer.technical_from%"
$baseHostOverride: "%app.base_host%"

$enablePaymentTaxSplit: "%app.workflow.enable_payment_tax_split%"
$stripeTaxAccount: "%app.stripe.tax_account%"
$taxOrderPostfix: "_TAX"

# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
Expand Down
1 change: 1 addition & 0 deletions config/services_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ services:
public: true
arguments:
$miraklClient: '@App\Tests\MiraklMockedHttpClient'
$taxOrderPostfix: 'string'

App\Service\StripeClient:
class: App\Service\StripeClient
Expand Down
6 changes: 3 additions & 3 deletions src/Command/SellerSettlementCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ private function processProvidedShopId(int $shopId)
);

$this->dispatchPayouts(
$this->sellerSettlementService->getPayoutsFromInvoices($invoices)
$this->sellerSettlementService->getPayoutsFromInvoices($invoices, $this->miraklClient)
);
}

Expand All @@ -114,7 +114,7 @@ private function processBacklog()
$this->dispatchTransfers($transfersByInvoiceId);

$payouts = $this->sellerSettlementService
->updatePayoutsFromInvoices($retriablePayouts, $invoices);
->updatePayoutsFromInvoices($retriablePayouts, $invoices, $this->miraklClient);
$this->dispatchPayouts($payouts);
}

Expand Down Expand Up @@ -151,7 +151,7 @@ private function processNewInvoices()
);

$this->dispatchPayouts(
$this->sellerSettlementService->getPayoutsFromInvoices($invoices)
$this->sellerSettlementService->getPayoutsFromInvoices($invoices, $this->miraklClient)
);

$checkpoint = $this->updateCheckpoint($invoices, $checkpoint);
Expand Down
2 changes: 2 additions & 0 deletions src/Entity/MiraklOrder.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,7 @@ abstract public function getAmountDue(): float;
abstract public function getAbortedAmount(): float;
abstract public function getOperatorCommission(): float;
abstract public function getRefundedOperatorCommission(StripeRefund $refund): float;
abstract public function getRefundedTax(StripeRefund $refund): float;
abstract public function getCurrency(): string;
abstract public function getOrderTaxTotal(): float;
}
49 changes: 49 additions & 0 deletions src/Entity/MiraklProductOrder.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,34 @@ public function getRefundedOperatorCommission(StripeRefund $refund): float
return 0;
}

public function getRefundedTax(StripeRefund $refund): float
{
foreach ($this->getOrderLines() as $line) {
if ($refund->getMiraklOrderLineId() === $line['order_line_id']) {
foreach ($line['refunds'] as $orderRefund) {
if ($refund->getMiraklRefundId() === $orderRefund['id']) {
$tax = $this->getRefundLineTaxes($orderRefund);
return $tax;
}
}
}
}

return 0;
}

protected function getRefundLineTaxes(array $refundLine): float
{
$taxes = 0;
$allTaxes = array_merge($refundLine['shipping_taxes'] ?? [], $refundLine['taxes'] ?? []);
foreach ($allTaxes as $tax) {
$taxes += (float) $tax['amount'];
}

return $taxes;
}


public function getCurrency(): string
{
return $this->order['currency_iso_code'];
Expand Down Expand Up @@ -156,4 +184,25 @@ protected function getOrderLineCanceledAmountWithTaxes(array $canceledOrderLines

return $canceledAmount;
}

public function getOrderTaxTotal(): float
{
$amount = 0;
foreach ($this->getOrderLines() as $orderLine) {
if (!in_array($orderLine['order_line_state'], ['REFUSED', 'CANCELED'])) {
$amount += $this->getOrderLineOrderTaxes($orderLine);
}
}
return $amount;
}

protected function getOrderLineOrderTaxes(array $orderLine): float
{
$taxes = 0;
$allTaxes = $orderLine['taxes'] ?? [];
foreach ($allTaxes as $tax) {
$taxes += (float) $tax['amount'];
}
return $taxes;
}
}
47 changes: 47 additions & 0 deletions src/Entity/MiraklServiceOrder.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,55 @@ public function getRefundedOperatorCommission(StripeRefund $refund): float
return 0;
}

public function getRefundedTax(StripeRefund $refund): float
{
foreach ($this->order['refunds'] as $orderRefund) {
if ($refund->getMiraklRefundId() === $orderRefund['id']) {
if ($refund->getMiraklRefundId() === $orderRefund['id']) {
return $this->getRefundLineTaxes($orderRefund);//$orderRefund['commission_total_amount'];
}
}
}

return 0;
}

protected function getRefundLineTaxes(array $refundLine): float
{
$taxes = 0;
$allTaxes = array_merge($refundLine['shipping_taxes'] ?? [], $refundLine['taxes'] ?? []);
foreach ($allTaxes as $tax) {
$taxes += (float) $tax['amount'];
}


return $taxes;
}

public function getCurrency(): string
{
return $this->order['currency_code'];
}

public function getOrderTaxTotal(): float
{
$taxes = 0;

if (!$this->isTaxIncluded()) {
foreach (($this->order['price']['taxes'] ?? []) as $tax) {
$taxes += (float) $tax['amount'];
}
}
return $taxes;
}

protected function getOrderLineOrderTaxes(array $orderLine): float
{
$taxes = 0;
$allTaxes = $orderLine['taxes'] ?? [];
foreach ($allTaxes as $tax) {
$taxes += (float) $tax['amount'];
}
return $taxes;
}
}
2 changes: 2 additions & 0 deletions src/Entity/StripeTransfer.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class StripeTransfer
public const TRANSFER_STATUS_REASON_REFUND_NOT_FOUND = 'Cannot find StripeRefund with ID %s';
public const TRANSFER_STATUS_REASON_REFUND_NOT_VALIDATED = 'Refund %s has yet to be validated';
public const TRANSFER_STATUS_REASON_TRANSFER_NOT_READY = 'Payment split has to occur before the transfer can be reversed for a refund';
public const TRANSFER_STATUS_REASON_ACCOUNT_NOT_FOUND = 'Cannot find Stripe account for ID %s';

// Transfer status reasons: aborted
public const TRANSFER_STATUS_REASON_ORDER_ABORTED = 'Order cannot be processed, status is %s';
Expand All @@ -54,6 +55,7 @@ class StripeTransfer
public const TRANSFER_STATUS_REASON_PAYMENT_REFUNDED = 'Payment %s has been fully refunded';
public const TRANSFER_STATUS_REASON_NO_SHOP_ID = 'No shop ID provided';
public const TRANSFER_STATUS_REASON_ORDER_REFUND_ABORTED = 'Refund %s has been aborted';
public const TRANSFER_STATUS_REASON_NO_ACCOUNT_ID = 'No stripe account ID provided';

// Transfer types
public const TRANSFER_PRODUCT_ORDER = 'TRANSFER_PRODUCT_ORDER';
Expand Down
28 changes: 23 additions & 5 deletions src/Factory/StripePayoutFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ class StripePayoutFactory implements LoggerAwareInterface
*/
private $accountMappingRepository;

/**
* @var MiraklClient
*/

public function __construct(
AccountMappingRepository $accountMappingRepository
) {
Expand All @@ -29,23 +33,23 @@ public function __construct(
* @param array $invoice
* @return StripePayout
*/
public function createFromInvoice(array $invoice): StripePayout
public function createFromInvoice(array $invoice, MiraklClient $mclient): StripePayout
{
$payout = new StripePayout();
$payout->setMiraklInvoiceId($invoice['invoice_id']);
$payout->setMiraklCreatedDate(
MiraklClient::getDatetimeFromString($invoice['date_created'])
);

return $this->updateFromInvoice($payout, $invoice);
return $this->updateFromInvoice($payout, $invoice, $mclient);
}

/**
* @param StripePayout $payout
* @param array $invoice
* @return StripePayout
*/
public function updateFromInvoice(StripePayout $payout, array $invoice): StripePayout
public function updateFromInvoice(StripePayout $payout, array $invoice, MiraklClient $mclient): StripePayout
{
// Payout already created
if ($payout->getPayoutId()) {
Expand All @@ -54,7 +58,7 @@ public function updateFromInvoice(StripePayout $payout, array $invoice): StripeP

// Amount and currency
try {
$payout->setAmount($this->getInvoiceAmount($invoice));
$payout->setAmount($this->getInvoiceAmount($invoice, $mclient));
$payout->setCurrency(strtolower($invoice['currency_iso_code']));
} catch (InvalidArgumentException $e) {
return $this->abortPayout($payout, $e->getMessage());
Expand Down Expand Up @@ -118,9 +122,12 @@ private function getAccountMapping(int $shopId): AccountMapping
* @param array $invoice
* @return int
*/
private function getInvoiceAmount(array $invoice): int
private function getInvoiceAmount(array $invoice, MiraklClient $mclient): int
{
$amount = $invoice['summary']['amount_transferred'] ?? 0;
$transactions = $mclient->getTransactionsForInvoce($invoice['invoice_id']);
$total_tax = $this->findTotalOrderTax($transactions);
$amount = $amount - $total_tax;
$amount = gmp_intval((string) ($amount * 100));
if ($amount <= 0) {
throw new InvalidArgumentException(sprintf(
Expand Down Expand Up @@ -178,4 +185,15 @@ private function markPayoutAsCreated(StripePayout $payout): StripePayout
$payout->setStatusReason(null);
return $payout->setStatus(StripePayout::PAYOUT_CREATED);
}

private function findTotalOrderTax($transactions)
{
$taxes=0;
foreach ($transactions as $trx) {
if ($trx['type']=='ORDER_AMOUNT_TAX') {
$taxes += (float) $trx['amount'];
}
}
return $taxes;
}
}
Loading

0 comments on commit 748a983

Please sign in to comment.