Download the PHP package jpmmartin/sylius-subscription-plugin without Composer

On this page you can find all versions of the php package jpmmartin/sylius-subscription-plugin. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package sylius-subscription-plugin

Sylius Subscription Plugin

Sell products by subscription in a standard Sylius 2 storefront. A variant offers one or more plans; the customer buys it once or on a plan, in the same cart as anything else. The store can also offer a few frequencies of its own, with which a customer repeats their whole cart. The lines of an order that renew on the same interval make one subscription, so every renewal is one normal Sylius order with all of them, charged without the customer present, with retries when a charge is declined.

Features

Requirements

Installation

On a store with Symfony Flex, as Sylius Standard is, step 1 takes steps 2 to 4 for you, from the plugin's recipe in symfony/recipes-contrib, and prints what is left: naming your payment methods in step 3, and steps 5 to 7. A store without Flex, or one that answered no when Composer offered the recipe, takes every step by hand; either way, the store ends with the same files.

  1. Require the plugin:

  2. Register the bundle in config/bundles.php, if Symfony Flex did not:

  3. Import its configuration and name the payment methods that may charge renewals, in config/packages/jpm_martin_sylius_subscription.yaml. The recipe writes this file, with no method named:

  4. Import its routes in config/routes/jpm_martin_sylius_subscription.yaml, which the recipe writes too. The admin routes go under the admin path, which puts them behind the admin firewall; the shop routes go under the locale prefix, exactly as Sylius's shop routes are imported, so the account's access control covers them:

  5. Let your order item carry the chosen plan, or the frequency its cart is repeated with: the class your store configures as sylius_order.resources.order_item.classes.model, for example:

    Until it does, the plugin offers no product by subscription, refuses a plan or a frequency asked for through the API as not offered, and the admin's dashboard and subscriptions list say what is left: no line chosen as a subscription can become a one-off purchase instead.

  6. Run the migrations. The plugin registers its own migrations namespace; its migrations, written with Doctrine's schema API rather than one platform's SQL, create its tables and the subscription_plan_id, subscription_frequency_id and subscription_trial_days columns of sylius_order_item:

  7. Process the due cycles on a schedule, with cron or Symfony Scheduler, as often as you want renewals to be charged. While step 5 is left, the command says so:

    The command dispatches one ProcessSubscriptionCycle message per due cycle on sylius.command_bus, so you may route that message to an asynchronous transport.

To take the plugin out, undo step 5 first: your order item would name the plugin's trait and interface, which are gone once it is removed, and the container would not compile. Taking the plugin's migrations down beforehand removes its tables and columns; composer remove then takes back the bundle and the two files.

Configuration reference

Charging renewals

A renewal is charged through JpmMartin\SyliusSubscriptionPlugin\Payment\RenewalChargerInterface: supports() says whether a payment method can be charged without the customer (checkout refuses subscriptions with any other), charge() charges a payment, and status() asks what became of a charge whose outcome was unknown, without charging again. Each answer is approved, declined (with the issuer's reason), not attempted (with a reason) or unknown. A decline, or a refused charge, may also carry the gateway's code for it, such as insufficient_funds: ChargeOutcome::declined($reason, $code). Each attempt keeps it, and the admin shows it next to the reason.

The default service

The default service supports the methods listed in payment_methods, and charges through Sylius's payment requests: it creates a capture payment request for the renewal payment (a status one to reconcile) and announces it, so the payment method's gateway handles it like any other payment request. The gateway's handler must be able to capture that payment without the customer, typically with a stored card or token; the outcome is read from the payment's state: completed is approved, failed or cancelled is declined. The reason is read from the payment request's response data (the first of reason, message, error or responsetext), and so is the code (the first of code, decline_code or error_code).

None of the official gateway plugins checked reports a code yet: the Stripe plugin (flux-se/sylius-stripe-plugin) writes only a reason when a payment fails, and the Adyen, Mollie and PayPal plugins charge through Payum rather than payment requests. To retry by code, have your gateway's handler put the code in the response data, or charge renewals with your own service.

Your own service

Implement RenewalChargerInterface and point the interface's alias at your service, or redefine the jpm_martin_sylius_subscription.payment.renewal_charger service. Everything in the plugin that charges or checks a payment method goes through that alias.

Failed cycles and manual retries

A cycle fails when its retries run out, a gate rejects it, its hold expires or none of its items can be sold that day. A failed cycle cancels its order if nothing was charged on it, which gives the reserved stock back, and the next cycle is scheduled on the subscription's calendar: a failure never cancels a subscription. After suspend_after_failed_cycles failed cycles in a row the subscription is suspended instead, and generates no cycles until an administrator reactivates it, or, when the last of them failed on a charge, its customer recovers it by paying; see Recovering a suspended subscription.

An administrator can retry a failed cycle of an active or suspended subscription from its page. The retry places a new order with the items that can be sold now and charges it once, with no automatic retries; an unknown outcome is reconciled like any other. If it is paid, the cycle is paid without moving the calendar; if it is declined, the cycle stays failed and its new order is cancelled. The subscription's state does not change, except when the retry pays the last cycle its items had left: an active subscription then completes, and a suspended one completes when it is reactivated.

If an administrator cancels a renewal order before it is paid, its cycle is cancelled and the next one scheduled, without counting as a failure: that renewal is skipped.

Suspending a subscription cancels its open cycle but leaves alone a retry still awaiting the gateway's answer, which the scheduler keeps reconciling. Cancelling the subscription cancels that retry too.

A suspended or a paused subscription is taken up again on the first date of its calendar after that day, even when that date is the one of the cycle the suspension or the pause cancelled.

Pausing and skipping

A customer pauses an active subscription from their account, and resumes it when they want: a pause has no end date. An administrator can do both on the customer's behalf. A pause is not a suspension:

A customer, or an administrator on their behalf, can also skip the next renewal of an active subscription while its order has not been placed: its cycle is cancelled, marked as skipped, and the next cycle keeps its date on the calendar. A skipped renewal is neither a failure nor a charge, so it does not use up a plan's maximum. Once the renewal's order is placed, for instance while its charge awaits a retry, it can no longer be skipped. max_consecutive_skips limits how many renewals in a row can be skipped: those skipped right before the open cycle, with no paid, failed or otherwise cancelled cycle between them. With none set, there is no limit. A skip cannot be undone.

The skip is a service, SubscriptionRenewalSkipperInterface: replace it to let customers skip another way.

A subscription within a minimum commitment cannot be paused by its customer, only skipped; see Minimum commitment. A prepaid delivery can be skipped and stays paid for; the charge of a prepaid block cannot, since it would skip its deliveries too: pause instead. See Prepaid deliveries.

Changing the address

A renewal goes to the addresses of the subscription's last order until they are changed. A customer changes them from their account, and an administrator from the subscription's page, for a subscription that is neither cancelled nor completed:

When the subscription's shipping method does not reach the new shipping address, the form lists the methods that do, the same ones the checkout would offer for its items there, each with what it would cost the next renewal at today's prices, and the subscription moves to the one chosen. When none reaches it, the change is refused and nothing changes. A subscription that ships nothing only changes its addresses.

Changing where a subscription ships is the first thing a stolen account does: listen to SubscriptionAddressChanged and tell the customer. The change is a service, SubscriptionAddressChangerInterface: replace it to change addresses another way.

Changing the items

A customer changes what an active subscription brings from their account, while its open cycle has no order yet: once the renewal's order is placed, for instance while its charge awaits a retry, that order keeps its items and nothing can be changed until the next cycle. The changes apply from the open cycle. On the "Change items" page, each item still renewing can:

A variant is offered when the channel sells it (enabled, its product in the channel, and at least one in stock when its stock is tracked), when it has terms of the subscription's interval of the item's kind (an enabled plan of its own for an item on a plan, the store's frequency, if the variant can be repeated, for an item repeated with it), when those terms' maximum of cycles is above what the item has been paid, and when no other renewing item has it.

The "Add a product" page lists, by product, every variant the channel sells with an enabled plan of the subscription's interval, or else that can be repeated with the store's frequency of that interval, and that no renewing item has: an item already renewing changes its quantity instead. The plan wins when a variant has both. The new item is frozen at the variant's current price less that discount, with no cycle paid.

When the changes raise what each renewal costs, the page shows the new total and asks the customer to accept the recurring charges again, and saves nothing until they do; see Consent to recurring charges. The "Add a product" page asks in the same step. Lowering a quantity, removing an item, or adding one that is free asks for nothing.

A removed item is not deleted: it stays on the subscription marked as removed, like one that reached its maximum of cycles, so the renewals that carried it keep showing it. It no longer renews, the frequency change leaves it alone, and the committed cycles do not count it. Its variant can be added again, as a new item. Nothing reserves stock: a renewal still skips what it cannot sell that day.

Listen to SubscriptionItemsChanged, which carries the totals before and after, to keep your own record of each change. The changes are a service, SubscriptionItemEditorInterface: replace it to offer other changes.

Retrying failed charges

What becomes of a charge that was declined or not attempted is decided by JpmMartin\SyliusSubscriptionPlugin\Cycle\RetryPolicyInterface: charge the same order again at a date, or fail the cycle. It is never asked about an administrator's retry, which is charged once.

The default policy retries after each of retry_delays, counted in days from the cycle's first attempt, and fails the cycle once they run out. A decline whose code is in final_decline_codes fails the cycle at once: retrying a stolen card only earns more declines. Codes are compared exactly and are the gateway's own, so the list is empty by default. With Stripe, for instance, whose decline codes include these:

For anything else, such as retrying by payment method, within hours, or by the gateway's advice, implement RetryPolicyInterface and point the interface's alias at your service. nextAttemptAt() is called once the failed attempt is among the cycle's attempts, with the charge's outcome and its code; return the date to charge again, or null to fail the cycle.

Unpaid orders expiring

Sylius's sylius:cancel-unpaid-orders cancels the orders left unpaid for longer than sylius_order.expiration.order, five days by default, which is shorter than the plugin's default retries. So that it never cuts a retry short, the plugin decorates sylius.updater.unpaid_orders_state with a version that leaves out the renewal orders whose cycle still awaits a retry: the retry policy decides when to give up on them, and failing the cycle cancels them. The order of a customer's recovery, which the plugin never charges, expires like any other. Every other order, initial orders included, expires as before. If your store decorates or replaces that service too, keep renewal orders awaiting payment out of it.

Paying a declined renewal

When a renewal's charge is declined, or could not be attempted, and a retry is to come, its customer can pay the renewal order on Sylius's own order payment page, sylius_shop_order_show (/order/{tokenValue}), with another card or another method:

A retry does not charge while the customer is paying the same order, so the renewal is not charged twice: a payment request of the pending payment still new or processing holds the retry back to the next run of the command. A customer who gives up on the gateway's page leaves that request behind, so it counts only until it has gone customer_payment_wait_minutes without changing, an hour by default; the retry then goes ahead. Only gateways that use Sylius's payment requests leave a request to find.

Changing the card

Without a renewal to pay, where the customer changes their card is the gateway integration's, since the card is the gateway's. An integration implements JpmMartin\SyliusSubscriptionPlugin\Payment\CardUpdateProviderInterface: supports() a subscription, usually by its payment method, and getUrl() of its own page. Tag it with jpm_martin_sylius_subscription.card_update_provider, or let autoconfiguration do it; the first that supports a subscription, by the tag's priority, gives the account's "Change card" link, and JpmMartin\SyliusSubscriptionPlugin\Payment\CardUpdateProviderRegistry gives it to your own templates or emails. The plugin registers none, so nothing is offered until one is.

Recovering a suspended subscription

A subscription suspended after suspend_after_failed_cycles failed cycles in a row, the last of them on a charge that was declined or could not be attempted, is suspended for unpaid renewals, and its customer can recover it from their account with "Pay and reactivate". When a gate, an expired hold or nothing to renew failed that last cycle, paying would not mend it: the subscription is suspended all the same, but only an administrator can reactivate it.

A suspension by an administrator is theirs to lift: its customer cannot recover it, and a recovery started before an administrator suspended the subscription again pays its cycle and leaves it suspended. RenewalPaymentLinkGeneratorInterface::generateRecovery($subscription) gives the absolute address of the store's notice of SubscriptionSuspended when forUnpaidRenewals is true: once signed in, the customer starts the recovery there and goes on to pay. It is null when the subscription cannot be recovered. The recovery is a service, SubscriptionRecoveryInterface: replace it to let customers recover another way.

Price updates

A subscription item keeps the price it was frozen at: a change of the catalogue does not reach it, so a temporary sale never touches the subscriptions. To pass a price change on, an administrator uses "Update subscription prices" on a plan's page, a store frequency's page, or the "Subscription" tab of a variant. Each item on it, of a subscription neither cancelled nor completed, is repriced as the cart prices a subscription line: its variant's current price in the subscription's channel, less the discount of its plan or store frequency. A preview first counts the subscriptions whose price would go up, go down or stay the same; confirming it needs the form's token.

With price_increase_acceptance: required, the customer accepts the new price from their account. If the first renewal at the new price comes before they do, the subscription is paused instead of renewed, and it can only be resumed by accepting it: the account offers "Accept the new price and resume". Each new increase asks again. Whether the default is notice or required in your country, and how many days of notice you owe, is for you to find out: the plugin gives you the choice, not the law. Tell your customers from SubscriptionPriceIncreaseAnnounced before asking for their acceptance; the plugin sends nothing. To ask for it in some channels or countries only, implement JpmMartin\SyliusSubscriptionPlugin\Pricing\PriceIncreaseAcceptancePolicyInterface and point the interface's alias at your service.

Confirming an update sends one JpmMartin\SyliusSubscriptionPlugin\Command\UpdateSubscriptionPrices per subscription on sylius.command_bus, each in its own transaction. They are handled at once by default; when an update may reach thousands of subscriptions, route the message to an asynchronous transport:

Introductory prices

A plan, on the variant's Subscription tab, or a store frequency can have an introductory price: an introductory discount, taken off the variant's price instead of the subscriber discount, for the first cycles of each new subscription. The form asks for it as "None", "First order only" or "The first N cycles", with the discount and, for the last, the number of cycles. The initial order is the first cycle: "First order only" discounts the order the customer places, and "The first 3 cycles" that order and the next two renewals.

Prepaid deliveries

A plan, on the variant's Subscription tab, or a store frequency can charge its deliveries by blocks: "Deliveries per charge" above 1, which an introductory offer cannot go with. A monthly plan at $18.00 a delivery charging 3 deliveries at a time charges $54.00 every three months and delivers every month.

PrepaidDeliveryPlaced carries cycleId, cycleNumber, orderId and deliveriesLeft, the deliveries still paid for after this one; see Events.

Minimum commitment

A plan, on the variant's Subscription tab, or a store frequency can have a minimum commitment: the cycles a subscriber pays, the initial order included, before they may cancel. "Six months at 20% off" is a monthly plan with a 20% discount and a commitment of 6.

Whether the law of your customers allows a minimum commitment on a consumer subscription, and for how long, is yours to find out: the plugin gives you the mechanism, off unless you set it. Say it in your consent text too (jpm_martin_sylius_subscription.consent.text), since that is what your customers accept; see Consent to recurring charges. There is no fee for leaving early, and nothing charges what is left of a commitment: an administrator who cancels ends it.

Free trials

A plan or a store frequency can start each new subscription with a free trial of some days: in its admin form, "Introductory offer" is then "A free trial of N days", which an introductory price cannot go with. The initial order charges nothing for it, the subscription is activated once the gateway authorizes that order's payment of 0, right after it is placed, and its first charge is the renewal on the day the trial ends; the calendar follows the interval from there. Subscribing on 1 March to a monthly plan with 14 days free, the customer is first charged on 15 March, then on 15 April.

Missed dates

A cycle's date comes from its subscription's calendar, so a late charge never moves the cycles after it. When a cycle is settled, paid, failed or cancelled, the next goes on the calendar's next date. That date may already have come: after jpm-martin:subscription:process-cycles stopped running for a while, or when a cycle of a short interval spent longer than its interval being retried. What becomes of such dates is decided by JpmMartin\SyliusSubscriptionPlugin\Schedule\MissedCyclePolicyInterface, and the default policy does what missed_cycles says:

The command says how many of the due cycles are more than one interval late, and each skip is logged as a warning with the subscription and its next date.

To decide it another way, implement MissedCyclePolicyInterface and point the interface's alias at your service. datesToSkip() is asked, before a cycle is scheduled, how many of the dates that have come to skip, from 0 up to all of them; isStillDue() is asked whether a scheduled cycle that is due is still processed.

Gates

Before a cycle places its order, every gate is asked whether it passes, waits until a date (with a reason) or is rejected (with a reason). A waiting cycle is held and asked again on every run until the earliest deadline it was given; if that deadline arrives, or a gate rejects it, the cycle fails. Without gates, every cycle passes.

Implement JpmMartin\SyliusSubscriptionPlugin\Gate\CycleGateInterface; with autoconfiguration the service is tagged jpm_martin_sylius_subscription.cycle_gate for you.

Consent to recurring charges

The text the customer accepts is the translation jpm_martin_sylius_subscription.consent.text (domain messages). Override it in your translations and raise consent_version when you change it: customers are then asked again, and each subscription keeps the version, text and date that were accepted for it.

The same text is asked again when a customer's changes to a subscription's items raise what each renewal costs, so write it to fit both moments. The subscription then keeps the version, text and date of that acceptance instead; the consent recorded on the initial order stays as the proof of the first.

Store frequencies and repeated carts

Shop API

The customer's subscriptions

The signed-in customer (the shop API's token, POST /api/v2/shop/customers/token) sees and manages their own subscriptions as their account does, with the same rules and the same services. A request without a token answers 401, and another customer's subscription 404.

Committed cycles

JpmMartin\SyliusSubscriptionPlugin\Query\CommittedCyclesQueryInterface::forProductVariant($variant, new \DateInterval('P3M')) returns, by date, the cycles the items of a variant in active subscriptions will renew within the horizon, each with its subscription, item, date and quantity, never past the cycles the item's plan or frequency still allows. It follows the missed cycle policy: a date the policy will skip is not returned, nor a cycle it will cancel.

Events

The plugin sends no email, SMS or any other notice to customers: what to say, in which words and through which channel is the store's. It publishes instead an event of its own at every moment of a subscription's life, as a Symfony Messenger message on sylius.event_bus, the bus Sylius publishes its own events on. Each event is a class of JpmMartin\SyliusSubscriptionPlugin\Event with public, read-only properties: identifiers and simple data, never entities, so it can go through an asynchronous transport. Every one implements SubscriptionEventInterface, whose getSubscriptionId() gives the subscription; listen to that interface to receive them all.

Event Published when Data besides subscriptionId
SubscriptionActivated the subscription is activated: its initial order was paid —
SubscriptionPaused it is paused, by its customer or an administrator on the customer's behalf —
SubscriptionResumed the paused subscription is resumed —
SubscriptionSuspended it is suspended, by an administrator or after failed cycles in a row forUnpaidRenewals: true when the last of those cycles failed on a charge, so its customer can recover it by paying
SubscriptionReactivated it is reactivated —
SubscriptionCancelled it is cancelled, by its customer or an administrator —
SubscriptionCompleted it ends: no item has a cycle left to renew —
SubscriptionFrequencyChanged its frequency changes intervalCount, intervalUnit (day, week, month or year)
SubscriptionAddressChanged its addresses change, by its customer or an administrator shippingMethodChanged
SubscriptionItemsChanged its customer changes, removes or adds items previousRenewalTotal, renewalTotal, in minor units of the subscription's currency
SubscriptionPaymentMethodChanged its customer paid a declined renewal with another method the plugin can charge, which it renews with from then on paymentMethodCode
SubscriptionPriceIncreaseAnnounced an administrator's price update announced an increase previousRenewalTotal, renewalTotal, appliesFrom, acceptanceRequired
SubscriptionPriceChanged a price update applied a decrease, or an announced increase applied when its first renewal was processed previousRenewalTotal, renewalTotal
RenewalUpcoming a renewal is renewal_notice_days away, once per cycle cycleId, cycleNumber, scheduledAt
TrialEnding with RenewalUpcoming, when that renewal is the first charge of a subscription that started with a free trial cycleId, cycleNumber, scheduledAt, renewalTotal, as for IntroductoryPriceEnding
IntroductoryPriceEnding with RenewalUpcoming, when that renewal is the first in which an item is charged its normal price after its introductory one cycleId, cycleNumber, scheduledAt, renewalTotal: what that renewal charges for its items, before taxes, shipping and promotions, in minor units of the subscription's currency
RenewalHeld a gate holds the cycle cycleId, cycleNumber, holdUntil, reason
RenewalOrderPlaced the cycle places its renewal order cycleId, cycleNumber, orderId
RenewalChargeDeclined a charge is declined or not attempted, and will be retried cycleId, cycleNumber, orderId, nextAttemptAt, reason, code
RenewalPaid the renewal order is paid cycleId, cycleNumber, orderId
PrepaidDeliveryPlaced a prepaid delivery places its order, without a charge; published instead of RenewalPaid cycleId, cycleNumber, orderId, deliveriesLeft
RenewalFailed the cycle fails: retries run out, a gate rejects it, its hold expires or nothing could be renewed cycleId, cycleNumber, orderId (null without an order), reason
RenewalRetried an administrator retries a failed cycle, or its customer starts recovering a suspended subscription; RenewalPaid or RenewalFailed follows with its new order cycleId, cycleNumber
RenewalSkipped the next renewal is skipped, by the customer or an administrator; published instead of RenewalCancelled cycleId, cycleNumber, scheduledAt, nextScheduledAt
RenewalCancelled the cycle is cancelled: its order was cancelled before being paid, the subscription was paused, suspended or cancelled, or the cycle was skipped as late cycleId, cycleNumber, orderId and reason, each null when there is none

The renewal events come from renewals only: the first cycle is the initial order, paid when the subscription is activated. The last retry that is declined publishes RenewalFailed, not RenewalChargeDeclined, and an administrator's retry, which is charged once, never publishes RenewalChargeDeclined. RenewalUpcoming is published by jpm-martin:subscription:process-cycles on its first run within renewal_notice_days of a scheduled cycle of an active subscription, and not for a cycle that is already due. So it reaches your customers only if the command runs at least once a day or so. A subscription that renews more often than that, every day for instance, has its next cycle within the notice as soon as it is scheduled: that cycle is announced in the same run that charged the one before, so less than renewal_notice_days ahead.

A notice of RenewalChargeDeclined can tell the customer where to pay the renewal themselves: JpmMartin\SyliusSubscriptionPlugin\Payment\RenewalPaymentLinkGeneratorInterface::generate($cycle) gives the absolute address of the order payment page, or null once the renewal can no longer be paid there. It is on the channel's hostname when the channel has one. Otherwise the host is the request's, and a worker handling events has none: set framework.router.default_uri, or the link points to localhost. Likewise, a notice of SubscriptionSuspended whose forUnpaidRenewals is true can give generateRecovery($subscription), where the customer recovers it; see Recovering a suspended subscription.

A handler, in a store with autoconfiguration:

When they are delivered:

So route the events you send notices from to an asynchronous transport, where a notice that fails is retried by the worker instead of stopping what happens to a subscription:

The plugin itself never stops a transition to publish its event: one of a subscription, cycle or renewal order that is not stored yet, which no flow of the plugin makes, publishes nothing and is logged. Only a handler of yours that throws, run synchronously, stops it, as above.

Known limitations

Development

The plugin is developed against Sylius's test application.

Configure the database in tests/TestApplication/.env.local and tests/TestApplication/.env.test.local.

Scenarios with JavaScript

The @javascript scenarios drive a headless Chrome listening on 127.0.0.1:9222 against the test application served on the URL in BEHAT_BASE_URL (http://127.0.0.1:8080/ unless tests/TestApplication/.env.test.local says otherwise). Any Chrome started with remote debugging works; in Docker:

If the product page answers 500 with an empty body, PHP-FPM has run out of memory: the test environment needs more than the 128M of a default php.ini. Raise memory_limit for the PHP the server uses, for example with an extra ini file: PHP_INI_SCAN_DIR=":/path/to/dir-with-a-memory-ini" symfony server:start ....

Another database

The tests use whatever DATABASE_URL says, and an environment variable wins over the .env files. To run them on MariaDB 11.4, for example:

Run one suite at a time per checkout: the test clock is a file of the test application (vendor/sylius/test-application/var/date.txt), so two runs from the same directory change each other's date.

Continuous integration

Two workflows:

The @javascript scenarios, MariaDB, the other versions of MySQL and PostgreSQL, PHP 8.3 and 8.5, and Symfony 6.4 are not checked on each change: run them locally, as above. Up to 1.0.0, each change was checked on all of them, with Sylius 2.2. Sylius Standard, which the install workflow creates, is on Sylius 2.2 until it moves to 2.3.

The tests charge renewals through a scripted gateway in the test application (tests/TestApplication/src/Payment), with payment requests handled synchronously and encrypted with a key kept for the tests only.

Versioning and changes

Released under Semantic Versioning: a caret constraint on this package is safe, and anything that would break an existing store arrives only in a major release with a written migration note. What changed in each release is in CHANGELOG.md; how a release is cut, and what counts as breaking, in RELEASING.md.

License

MIT. See LICENSE.


All versions of sylius-subscription-plugin with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
sylius/sylius Version ^2.2
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package jpmmartin/sylius-subscription-plugin contains the following files

Loading the files please wait ...