From 2463d3af668cb923f8e569d7b88be27e4d2ea8d6 Mon Sep 17 00:00:00 2001 From: eliot lauger Date: Mon, 20 Jan 2025 12:27:14 +0100 Subject: [PATCH 1/6] feat: Add oxiMailing service and bruno collection for all services --- README.md | 26 +++- .../Brevo/Add existing contacts to a list.bru | 23 ++++ bruno/subscribeme/Brevo/Get all the lists.bru | 16 +++ .../Brevo/Get contacts in a list.bru | 15 +++ .../MailChimp/Add member to list.bru | 26 ++++ bruno/subscribeme/MailChimp/Get all lists.bru | 16 +++ .../MailChimp/List members info.bru | 16 +++ .../MailJet/Add contact in a list.bru | 11 ++ .../MailJet/Get contacts in a list.bru | 15 +++ .../subscribeme/MailJet/Get contactsList.bru | 16 +++ ... new contacts into the specified list..bru | 20 +++ .../Delete the specified contacts..bru | 20 +++ .../OxiMailing/Get your contact lists..bru | 16 +++ ...t your contacts in the specified list..bru | 20 +++ bruno/subscribeme/bruno.json | 9 ++ bruno/subscribeme/environments/PROD.bru | 16 +++ src/SubscribeMe/Factory.php | 3 + .../Subscriber/OxiMailingSubscriber.php | 78 ++++++++++++ tests/OxiMailingMailerTest.php | 116 ++++++++++++++++++ 19 files changed, 477 insertions(+), 1 deletion(-) create mode 100644 bruno/subscribeme/Brevo/Add existing contacts to a list.bru create mode 100644 bruno/subscribeme/Brevo/Get all the lists.bru create mode 100644 bruno/subscribeme/Brevo/Get contacts in a list.bru create mode 100644 bruno/subscribeme/MailChimp/Add member to list.bru create mode 100644 bruno/subscribeme/MailChimp/Get all lists.bru create mode 100644 bruno/subscribeme/MailChimp/List members info.bru create mode 100644 bruno/subscribeme/MailJet/Add contact in a list.bru create mode 100644 bruno/subscribeme/MailJet/Get contacts in a list.bru create mode 100644 bruno/subscribeme/MailJet/Get contactsList.bru create mode 100644 bruno/subscribeme/OxiMailing/Add new contacts into the specified list..bru create mode 100644 bruno/subscribeme/OxiMailing/Delete the specified contacts..bru create mode 100644 bruno/subscribeme/OxiMailing/Get your contact lists..bru create mode 100644 bruno/subscribeme/OxiMailing/Get your contacts in the specified list..bru create mode 100644 bruno/subscribeme/bruno.json create mode 100644 bruno/subscribeme/environments/PROD.bru create mode 100644 src/SubscribeMe/Subscriber/OxiMailingSubscriber.php create mode 100644 tests/OxiMailingMailerTest.php diff --git a/README.md b/README.md index 7ce27a9..5cc9be1 100644 --- a/README.md +++ b/README.md @@ -322,4 +322,28 @@ $variables = [ 'personalmessage' => 'Happy birthday!' ]; $subscriber->sendTransactionalEmail($emails, $templateEmail, $variables); -``` \ No newline at end of file +``` + + +## OxiMailing + +### OxiMailing subscriber options + +```php +$subscriber = $factory->createFor('oxymailing'); +// OxiMailing requires an API Key and an API Secret +$subscriber->setApiKey('mailjet_api_key'); +$subscriber->setApiSecret('mailjet_api_secret') +// OxiMailing list identifiers are int. You can only subscribe user to one list +$subscriber->setContactListId('123'); +// OxiMailing Accept 3 modes +//Allows you to explain what to do with new duplicates : +//- ignore : remove duplicates +//- insert : don't do anything (all contacts are imported even duplicates) +//- update : update existing contacts information rather than adding duplicates +$subscriber->subscribe('hello@super.test', ['mode' => 'update']); +``` + +### OxiMailing sender transactional email options + +OxiMailing does not support transactional email, we throw an `UnsupportedTransactionalEmailPlatformException`. \ No newline at end of file diff --git a/bruno/subscribeme/Brevo/Add existing contacts to a list.bru b/bruno/subscribeme/Brevo/Add existing contacts to a list.bru new file mode 100644 index 0000000..7c79cce --- /dev/null +++ b/bruno/subscribeme/Brevo/Add existing contacts to a list.bru @@ -0,0 +1,23 @@ +meta { + name: Add existing contacts to a list + type: http + seq: 2 +} + +post { + url: {{brevo_base_url}}/v3/contacts/lists/:listId/contacts/add + body: json + auth: none +} + +params:path { + listId: +} + +body:json { + { + "emails": [ + "john.smith@contact.com" + ] + } +} diff --git a/bruno/subscribeme/Brevo/Get all the lists.bru b/bruno/subscribeme/Brevo/Get all the lists.bru new file mode 100644 index 0000000..731e384 --- /dev/null +++ b/bruno/subscribeme/Brevo/Get all the lists.bru @@ -0,0 +1,16 @@ +meta { + name: Get all the lists + type: http + seq: 1 +} + +get { + url: {{brevo_base_url}}/v3/contacts/lists + body: none + auth: basic +} + +auth:basic { + username: {{brevo_login}} + password: {{brevo_password}} +} diff --git a/bruno/subscribeme/Brevo/Get contacts in a list.bru b/bruno/subscribeme/Brevo/Get contacts in a list.bru new file mode 100644 index 0000000..d3fad4d --- /dev/null +++ b/bruno/subscribeme/Brevo/Get contacts in a list.bru @@ -0,0 +1,15 @@ +meta { + name: Get contacts in a list + type: http + seq: 3 +} + +get { + url: {{brevo_base_url}}/v3/contacts/lists/:listId/contacts + body: none + auth: none +} + +params:path { + listId: +} diff --git a/bruno/subscribeme/MailChimp/Add member to list.bru b/bruno/subscribeme/MailChimp/Add member to list.bru new file mode 100644 index 0000000..769e494 --- /dev/null +++ b/bruno/subscribeme/MailChimp/Add member to list.bru @@ -0,0 +1,26 @@ +meta { + name: Add member to list + type: http + seq: 2 +} + +post { + url: {{mailchimp_base_url}}/lists/:listId/members + body: json + auth: basic +} + +params:path { + listId: 123 +} + +auth:basic { + username: {{mailchimp_login}} + password: {{mailchimp_password}} +} + +body:json { + { + "email_address": "john.smith@contact.com" + } +} \ No newline at end of file diff --git a/bruno/subscribeme/MailChimp/Get all lists.bru b/bruno/subscribeme/MailChimp/Get all lists.bru new file mode 100644 index 0000000..ab360a9 --- /dev/null +++ b/bruno/subscribeme/MailChimp/Get all lists.bru @@ -0,0 +1,16 @@ +meta { + name: Get all lists + type: http + seq: 3 +} + +get { + url: {{mailchimp_base_url}}/lists/ + body: none + auth: basic +} + +auth:basic { + username: {{mailchimp_login}} + password: {{mailchimp_password}} +} \ No newline at end of file diff --git a/bruno/subscribeme/MailChimp/List members info.bru b/bruno/subscribeme/MailChimp/List members info.bru new file mode 100644 index 0000000..436f97d --- /dev/null +++ b/bruno/subscribeme/MailChimp/List members info.bru @@ -0,0 +1,16 @@ +meta { + name: List members info + type: http + seq: 1 +} + +get { + url: {{mailchimp_base_url}}/lists/:listId/members + body: none + auth: basic +} + +auth:basic { + username: {{mailchimp_login}} + password: {{mailchimp_password}} +} diff --git a/bruno/subscribeme/MailJet/Add contact in a list.bru b/bruno/subscribeme/MailJet/Add contact in a list.bru new file mode 100644 index 0000000..f1b4187 --- /dev/null +++ b/bruno/subscribeme/MailJet/Add contact in a list.bru @@ -0,0 +1,11 @@ +meta { + name: Add contact in a list + type: http + seq: 2 +} + +post { + url: {{mailjet_base_url}} + body: none + auth: none +} diff --git a/bruno/subscribeme/MailJet/Get contacts in a list.bru b/bruno/subscribeme/MailJet/Get contacts in a list.bru new file mode 100644 index 0000000..e8c7056 --- /dev/null +++ b/bruno/subscribeme/MailJet/Get contacts in a list.bru @@ -0,0 +1,15 @@ +meta { + name: Get contacts in a list + type: http + seq: 3 +} + +get { + url: {{mailjet_base_url}}/v3/REST/contactslist/:listId + body: none + auth: none +} + +params:path { + listId: +} diff --git a/bruno/subscribeme/MailJet/Get contactsList.bru b/bruno/subscribeme/MailJet/Get contactsList.bru new file mode 100644 index 0000000..f0d5a0f --- /dev/null +++ b/bruno/subscribeme/MailJet/Get contactsList.bru @@ -0,0 +1,16 @@ +meta { + name: Get contactsList + type: http + seq: 1 +} + +get { + url: {{mailjet_base_url}}/v3/REST/contactslist/ + body: none + auth: basic +} + +auth:basic { + username: {{mailjet_login}} + password: {{mailjet_password}} +} diff --git a/bruno/subscribeme/OxiMailing/Add new contacts into the specified list..bru b/bruno/subscribeme/OxiMailing/Add new contacts into the specified list..bru new file mode 100644 index 0000000..cafea53 --- /dev/null +++ b/bruno/subscribeme/OxiMailing/Add new contacts into the specified list..bru @@ -0,0 +1,20 @@ +meta { + name: Add new contacts into the specified list. + type: http + seq: 1 +} + +post { + url: {{oximailing_base_url}}/lists/:listId/contacts + body: none + auth: basic +} + +params:path { + listId: 841 +} + +auth:basic { + username: {{oximailing_login}} + password: {{oximailing_password}} +} diff --git a/bruno/subscribeme/OxiMailing/Delete the specified contacts..bru b/bruno/subscribeme/OxiMailing/Delete the specified contacts..bru new file mode 100644 index 0000000..06688fa --- /dev/null +++ b/bruno/subscribeme/OxiMailing/Delete the specified contacts..bru @@ -0,0 +1,20 @@ +meta { + name: Delete the specified contacts. + type: http + seq: 3 +} + +delete { + url: {{oximailing_base_url}}/lists/:listId/contacts + body: none + auth: basic +} + +params:path { + listId: 841 +} + +auth:basic { + username: {{oximailing_login}} + password: {{oximailing_password}} +} diff --git a/bruno/subscribeme/OxiMailing/Get your contact lists..bru b/bruno/subscribeme/OxiMailing/Get your contact lists..bru new file mode 100644 index 0000000..bd7ea0a --- /dev/null +++ b/bruno/subscribeme/OxiMailing/Get your contact lists..bru @@ -0,0 +1,16 @@ +meta { + name: Get your contact lists. + type: http + seq: 4 +} + +get { + url: {{oximailing_base_url}}/lists + body: none + auth: basic +} + +auth:basic { + username: {{oximailing_login}} + password: {{oximailing_password}} +} diff --git a/bruno/subscribeme/OxiMailing/Get your contacts in the specified list..bru b/bruno/subscribeme/OxiMailing/Get your contacts in the specified list..bru new file mode 100644 index 0000000..eb78a2e --- /dev/null +++ b/bruno/subscribeme/OxiMailing/Get your contacts in the specified list..bru @@ -0,0 +1,20 @@ +meta { + name: Get your contacts in the specified list. + type: http + seq: 2 +} + +get { + url: {{oximailing_base_url}}/lists/:listId/contacts + body: none + auth: basic +} + +params:path { + listId: 841 +} + +auth:basic { + username: {{oximailing_login}} + password: {{oximailing_password}} +} diff --git a/bruno/subscribeme/bruno.json b/bruno/subscribeme/bruno.json new file mode 100644 index 0000000..af16c3f --- /dev/null +++ b/bruno/subscribeme/bruno.json @@ -0,0 +1,9 @@ +{ + "version": "1", + "name": "subscribeme", + "type": "collection", + "ignore": [ + "node_modules", + ".git" + ] +} \ No newline at end of file diff --git a/bruno/subscribeme/environments/PROD.bru b/bruno/subscribeme/environments/PROD.bru new file mode 100644 index 0000000..0ea83ee --- /dev/null +++ b/bruno/subscribeme/environments/PROD.bru @@ -0,0 +1,16 @@ +vars { + oximailing_base_url: https://api.oximailing.com + mailjet_base_url: https://api.mailjet.com + brevo_base_url: https://api.brevo.com + mailchimp_base_url: https://us16.api.mailchimp.com/3.0 +} +vars:secret [ + oximailing_login, + oximailing_password, + mailjet_login, + mailjet_password, + brevo_login, + brevo_password, + mailchimp_login, + mailchimp_password +] diff --git a/src/SubscribeMe/Factory.php b/src/SubscribeMe/Factory.php index b60cfd2..1e3bc11 100644 --- a/src/SubscribeMe/Factory.php +++ b/src/SubscribeMe/Factory.php @@ -11,6 +11,7 @@ use SubscribeMe\Subscriber\BrevoSubscriber; use SubscribeMe\Subscriber\MailchimpSubscriber; use SubscribeMe\Subscriber\MailjetSubscriber; +use SubscribeMe\Subscriber\OxiMailingSubscriber; use SubscribeMe\Subscriber\SubscriberInterface; use SubscribeMe\Subscriber\YmlpSubscriber; @@ -38,6 +39,8 @@ public function createFor(string $platform): SubscriberInterface return new BrevoDoubleOptInSubscriber($this->client, $this->requestFactory, $this->streamFactory); case 'ymlp': return new YmlpSubscriber($this->client, $this->requestFactory, $this->streamFactory); + case 'oximailing': + return new OxiMailingSubscriber($this->client, $this->requestFactory, $this->streamFactory); } throw new \InvalidArgumentException('No subscriber class found for ' . $platform); } diff --git a/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php b/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php new file mode 100644 index 0000000..c4bb3ea --- /dev/null +++ b/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php @@ -0,0 +1,78 @@ +getApiKey())) { + throw new ApiCredentialsException(); + } + + if (!is_string($this->getApiSecret())) { + throw new ApiCredentialsException(); + } + + $body = [ + 'mode' => $options['mode'], + 'contacts' => [$email], + ]; + + $uri = 'https://https://api.oximailing.com/lists/' . $this->getContactListId() . '/contacts'; + try { + $bodyStreamed = $this->getStreamFactory()->createStream(json_encode($body, JSON_THROW_ON_ERROR)); + + $request = $this->getRequestFactory() + ->createRequest('POST', $uri) + ->withBody($bodyStreamed) + ->withAddedHeader('Content-Type', 'application/json') + ->withAddedHeader('User-Agent', 'rezozero/subscribeme') + ->withAddedHeader('Authorization', 'Basic '.base64_encode(sprintf('%s:%s', $this->getApiKey(), $this->getApiSecret()))); + + $res = $this->getClient()->sendRequest($request); + + if ($res->getStatusCode() === 200 || $res->getStatusCode() === 201) { + /** @var array $body */ + $body = json_decode($res->getBody()->getContents(), true); + if ($body['Total'] >= 1) { + return $body['Data'][0]['ContactID']; + } + } + } catch (ClientExceptionInterface $exception) { + throw new CannotSubscribeException($exception->getMessage(), $exception); + } + + return false; + } + + /** + * @inheritdoc + */ + public function sendTransactionalEmail(array $emails, string|int $emailTemplateId, array $variables = []): string + { + throw new UnsupportedTransactionalEmailPlatformException(); + } +} diff --git a/tests/OxiMailingMailerTest.php b/tests/OxiMailingMailerTest.php new file mode 100644 index 0000000..d5f2dfc --- /dev/null +++ b/tests/OxiMailingMailerTest.php @@ -0,0 +1,116 @@ +setDefaultResponse(new Response(200, [], json_encode(['Total' => 1, 'Data' => [['ContactID' => 1]] ], JSON_THROW_ON_ERROR))); + + $oxiMailingSubscriber = new OxiMailingSubscriber($client, $factory, $factory); + + $oxiMailingSubscriber->setApiKey('3f62c1f4-efb7-4bc7-b76d-0c2217d307b0'); + $oxiMailingSubscriber->setApiSecret('df30148e-6cda-43ae-8665-9904f5f4f12a'); + $returnCode = $oxiMailingSubscriber->subscribe("tester@oximailing.com", ['mode' => 'update']); + + $requests = $client->getRequests(); + + $body = [ + 'mode' => 'update', + 'contacts' => ['tester@oximailing.com'], + ]; + $body = json_encode($body); + + $this->assertEquals(1, $returnCode); + $this->assertCount(1, $requests); + $content = $requests[0]->getBody()->getContents(); + $this->assertEquals('application/json', $requests[0]->getHeaders()['Content-Type'][0]); + $this->assertEquals('Basic ' . base64_encode(sprintf('%s:%s', '3f62c1f4-efb7-4bc7-b76d-0c2217d307b0', 'df30148e-6cda-43ae-8665-9904f5f4f12a')), $requests[0]->getHeaders()['Authorization'][0]); + $this->assertEquals('rezozero/subscribeme', $requests[0]->getHeaders()['User-Agent'][0]); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertJsonStringEqualsJsonString($body ?: '{}', $content); + $this->assertStringContainsString('api.oximailing.com/', $requests[0]->getUri()->getPath()); + } + + /** + * @throws JsonException + */ + public function testSubscribeWithCodeError(): void + { + $client = new Client(); + $factory = new Psr17Factory(); + + $client->setDefaultResponse(new Response(400)); + + $oxiMailingSubscriber = new OxiMailingSubscriber($client, $factory, $factory); + + $oxiMailingSubscriber->setApiKey('3f62c1f4-efb7-4bc7-b76d-0c2217d307b0'); + $oxiMailingSubscriber->setApiSecret('df30148e-6cda-43ae-8665-9904f5f4f12a'); + $returnCode = $oxiMailingSubscriber->subscribe("tester@oximailing.com", ['mode' => 'update']); + + $requests = $client->getRequests(); + + $body = [ + 'mode' => 'update', + 'contacts' => ['tester@oximailing.com'], + ]; + $body = json_encode($body); + + $this->assertFalse($returnCode); + $this->assertCount(1, $requests); + $content = $requests[0]->getBody()->getContents(); + $this->assertEquals('application/json', $requests[0]->getHeaders()['Content-Type'][0]); + $this->assertEquals('Basic ' . base64_encode(sprintf('%s:%s', '3f62c1f4-efb7-4bc7-b76d-0c2217d307b0', 'df30148e-6cda-43ae-8665-9904f5f4f12a')), $requests[0]->getHeaders()['Authorization'][0]); + $this->assertEquals('rezozero/subscribeme', $requests[0]->getHeaders()['User-Agent'][0]); + $this->assertEquals('POST', $requests[0]->getMethod()); + $this->assertJsonStringEqualsJsonString($body ?: '{}', $content); + $this->assertStringContainsString('api.oximailing.com/', $requests[0]->getUri()->getPath()); + } + + /** + * @throws JsonException + */ + public function testSendTransactionalEmail(): void + { + $this->expectException(UnsupportedTransactionalEmailPlatformException::class); + $client = new Client(); + $factory = new Psr17Factory(); + $oxiMailingSubscriber = new OxiMailingSubscriber($client, $factory, $factory); + $emails = [ + new EmailAddress('jdoe@example.com') + ]; + $oxiMailingSubscriber->sendTransactionalEmail($emails, '1'); + } + + /** + * @throws JsonException + */ + public function testExceptionApiKey(): void + { + $this->expectException(ApiCredentialsException::class); + $client = new Client(); + $factory = new Psr17Factory(); + $oxiMailingSubscriber = new OxiMailingSubscriber($client, $factory, $factory); + $emails = 'passenger1@mailjet.com'; + $oxiMailingSubscriber->subscribe($emails, ['mode' => 'update']); + } +} From 57d407ebdd0662db16c4fd4fee2c98474f623c92 Mon Sep 17 00:00:00 2001 From: eliot lauger Date: Mon, 20 Jan 2025 16:57:11 +0100 Subject: [PATCH 2/6] chore: Fix OxiMailingSubscriber and the test associate --- README.md | 6 ++-- ... new contacts into the specified list..bru | 24 +++++++++++++++- .../Delete the specified contacts..bru | 10 ++++++- .../Subscriber/OxiMailingSubscriber.php | 28 +++++++++---------- tests/OxiMailingMailerTest.php | 26 +++-------------- 5 files changed, 52 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 5cc9be1..9ec33b4 100644 --- a/README.md +++ b/README.md @@ -330,10 +330,10 @@ $subscriber->sendTransactionalEmail($emails, $templateEmail, $variables); ### OxiMailing subscriber options ```php -$subscriber = $factory->createFor('oxymailing'); +$subscriber = $factory->createFor('oximailing'); // OxiMailing requires an API Key and an API Secret -$subscriber->setApiKey('mailjet_api_key'); -$subscriber->setApiSecret('mailjet_api_secret') +$subscriber->setApiKey('oximailing_api_key'); +$subscriber->setApiSecret('oximailing_api_secret') // OxiMailing list identifiers are int. You can only subscribe user to one list $subscriber->setContactListId('123'); // OxiMailing Accept 3 modes diff --git a/bruno/subscribeme/OxiMailing/Add new contacts into the specified list..bru b/bruno/subscribeme/OxiMailing/Add new contacts into the specified list..bru index cafea53..2bd6141 100644 --- a/bruno/subscribeme/OxiMailing/Add new contacts into the specified list..bru +++ b/bruno/subscribeme/OxiMailing/Add new contacts into the specified list..bru @@ -5,11 +5,16 @@ meta { } post { - url: {{oximailing_base_url}}/lists/:listId/contacts + url: {{oximailing_base_url}}/lists/:listId/contacts?contacts={"email1@example.com":{"customerName":"Dupont","customerId":123}} &method=ignore body: none auth: basic } +params:query { + contacts: {"email1@example.com":{"customerName":"Dupont","customerId":123}} + method: ignore +} + params:path { listId: 841 } @@ -18,3 +23,20 @@ auth:basic { username: {{oximailing_login}} password: {{oximailing_password}} } + +body:json { + { + "contacts" : { + "email@rezo-zero.com": { + "firstName": "firstName", + "lastName": "lastName" + } + }, + "mode" : "ignore" + } +} + +body:multipart-form { + ~contacts: {"email1@example.com":{"customerName":"Dupont","customerId":123}} + ~mode: ignore +} diff --git a/bruno/subscribeme/OxiMailing/Delete the specified contacts..bru b/bruno/subscribeme/OxiMailing/Delete the specified contacts..bru index 06688fa..143cbec 100644 --- a/bruno/subscribeme/OxiMailing/Delete the specified contacts..bru +++ b/bruno/subscribeme/OxiMailing/Delete the specified contacts..bru @@ -5,11 +5,15 @@ meta { } delete { - url: {{oximailing_base_url}}/lists/:listId/contacts + url: {{oximailing_base_url}}/lists/:listId/contacts?emails=email1@example.com body: none auth: basic } +params:query { + emails: email1@example.com +} + params:path { listId: 841 } @@ -18,3 +22,7 @@ auth:basic { username: {{oximailing_login}} password: {{oximailing_password}} } + +body:multipart-form { + emails: email1@example.com +} diff --git a/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php b/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php index c4bb3ea..7c1ba7b 100644 --- a/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php +++ b/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php @@ -4,15 +4,10 @@ namespace SubscribeMe\Subscriber; -use JsonException; use Psr\Http\Client\ClientExceptionInterface; -use SubscribeMe\Exception\ApiResponseException; -use SubscribeMe\Exception\CannotSendTransactionalEmailException; -use SubscribeMe\Exception\CannotSubscribeException; use SubscribeMe\Exception\ApiCredentialsException; +use SubscribeMe\Exception\CannotSubscribeException; use SubscribeMe\Exception\UnsupportedTransactionalEmailPlatformException; -use SubscribeMe\GDPR\UserConsent; -use SubscribeMe\ValueObject\EmailAddress; class OxiMailingSubscriber extends AbstractSubscriber { @@ -36,29 +31,32 @@ public function subscribe(string $email, array $options, array $userConsents = [ throw new ApiCredentialsException(); } + $mode = $options['mode'] ?? 'ignore'; + unset($options['mode']); + + $contacts[$email] = $options; + $body = [ - 'mode' => $options['mode'], - 'contacts' => [$email], + 'mode' => $mode, + 'contacts' => json_encode($contacts, JSON_THROW_ON_ERROR), ]; + $queryParams = http_build_query($body); - $uri = 'https://https://api.oximailing.com/lists/' . $this->getContactListId() . '/contacts'; + $uri = 'https://https://api.oximailing.com/lists/' . $this->getContactListId() . '/contacts?' . $queryParams; try { - $bodyStreamed = $this->getStreamFactory()->createStream(json_encode($body, JSON_THROW_ON_ERROR)); - $request = $this->getRequestFactory() ->createRequest('POST', $uri) - ->withBody($bodyStreamed) - ->withAddedHeader('Content-Type', 'application/json') ->withAddedHeader('User-Agent', 'rezozero/subscribeme') ->withAddedHeader('Authorization', 'Basic '.base64_encode(sprintf('%s:%s', $this->getApiKey(), $this->getApiSecret()))); $res = $this->getClient()->sendRequest($request); + if ($res->getStatusCode() === 200 || $res->getStatusCode() === 201) { /** @var array $body */ $body = json_decode($res->getBody()->getContents(), true); - if ($body['Total'] >= 1) { - return $body['Data'][0]['ContactID']; + if ($body['added'] == 1 || $body['ignored'] == 1 || $body['updated'] == 1) { + return true; } } } catch (ClientExceptionInterface $exception) { diff --git a/tests/OxiMailingMailerTest.php b/tests/OxiMailingMailerTest.php index d5f2dfc..657a7e6 100644 --- a/tests/OxiMailingMailerTest.php +++ b/tests/OxiMailingMailerTest.php @@ -24,30 +24,21 @@ public function testSubscribe(): void $client = new Client(); $factory = new Psr17Factory(); - $client->setDefaultResponse(new Response(200, [], json_encode(['Total' => 1, 'Data' => [['ContactID' => 1]] ], JSON_THROW_ON_ERROR))); + $client->setDefaultResponse(new Response(200, [], json_encode(['invalid' => 0, 'added' => 1, 'ignored' => 0, 'updated' => 0], JSON_THROW_ON_ERROR))); $oxiMailingSubscriber = new OxiMailingSubscriber($client, $factory, $factory); $oxiMailingSubscriber->setApiKey('3f62c1f4-efb7-4bc7-b76d-0c2217d307b0'); $oxiMailingSubscriber->setApiSecret('df30148e-6cda-43ae-8665-9904f5f4f12a'); - $returnCode = $oxiMailingSubscriber->subscribe("tester@oximailing.com", ['mode' => 'update']); + $returnCode = $oxiMailingSubscriber->subscribe("tester@oximailing.com", ['mode' => 'ignored', 'firstName' => 'John', 'lastName' => 'Doe']); $requests = $client->getRequests(); - $body = [ - 'mode' => 'update', - 'contacts' => ['tester@oximailing.com'], - ]; - $body = json_encode($body); - - $this->assertEquals(1, $returnCode); + $this->assertTrue($returnCode); $this->assertCount(1, $requests); - $content = $requests[0]->getBody()->getContents(); - $this->assertEquals('application/json', $requests[0]->getHeaders()['Content-Type'][0]); $this->assertEquals('Basic ' . base64_encode(sprintf('%s:%s', '3f62c1f4-efb7-4bc7-b76d-0c2217d307b0', 'df30148e-6cda-43ae-8665-9904f5f4f12a')), $requests[0]->getHeaders()['Authorization'][0]); $this->assertEquals('rezozero/subscribeme', $requests[0]->getHeaders()['User-Agent'][0]); $this->assertEquals('POST', $requests[0]->getMethod()); - $this->assertJsonStringEqualsJsonString($body ?: '{}', $content); $this->assertStringContainsString('api.oximailing.com/', $requests[0]->getUri()->getPath()); } @@ -65,24 +56,15 @@ public function testSubscribeWithCodeError(): void $oxiMailingSubscriber->setApiKey('3f62c1f4-efb7-4bc7-b76d-0c2217d307b0'); $oxiMailingSubscriber->setApiSecret('df30148e-6cda-43ae-8665-9904f5f4f12a'); - $returnCode = $oxiMailingSubscriber->subscribe("tester@oximailing.com", ['mode' => 'update']); + $returnCode = $oxiMailingSubscriber->subscribe("tester@oximailing.com", ['mode' => 'ignored', 'firstName' => 'John', 'lastName' => 'Doe']); $requests = $client->getRequests(); - $body = [ - 'mode' => 'update', - 'contacts' => ['tester@oximailing.com'], - ]; - $body = json_encode($body); - $this->assertFalse($returnCode); $this->assertCount(1, $requests); - $content = $requests[0]->getBody()->getContents(); - $this->assertEquals('application/json', $requests[0]->getHeaders()['Content-Type'][0]); $this->assertEquals('Basic ' . base64_encode(sprintf('%s:%s', '3f62c1f4-efb7-4bc7-b76d-0c2217d307b0', 'df30148e-6cda-43ae-8665-9904f5f4f12a')), $requests[0]->getHeaders()['Authorization'][0]); $this->assertEquals('rezozero/subscribeme', $requests[0]->getHeaders()['User-Agent'][0]); $this->assertEquals('POST', $requests[0]->getMethod()); - $this->assertJsonStringEqualsJsonString($body ?: '{}', $content); $this->assertStringContainsString('api.oximailing.com/', $requests[0]->getUri()->getPath()); } From 9b4f7ae76c6f1c2642472f658ec5e4cb737dd399 Mon Sep 17 00:00:00 2001 From: eliot lauger Date: Tue, 21 Jan 2025 09:37:53 +0100 Subject: [PATCH 3/6] chore: Fix UnitTest and add unsubscribe method --- ...nsupportedUnsubscribePlatformException.php | 13 +++++ .../Subscriber/BrevoDoubleOptInSubscriber.php | 1 + .../Subscriber/BrevoSubscriber.php | 48 ++++++++++++++++++ .../Subscriber/MailchimpSubscriber.php | 13 +++++ .../Subscriber/MailjetSubscriber.php | 13 +++++ .../Subscriber/OxiMailingSubscriber.php | 48 +++++++++++++++++- .../Subscriber/SubscriberInterface.php | 11 +++- src/SubscribeMe/Subscriber/YmlpSubscriber.php | 13 +++++ tests/MailjetMailerTest.php | 2 + tests/OxiMailingMailerTest.php | 50 ++++++++++++++++++- tests/YmlpMailerTest.php | 6 ++- 11 files changed, 212 insertions(+), 6 deletions(-) create mode 100644 src/SubscribeMe/Exception/UnsupportedUnsubscribePlatformException.php diff --git a/src/SubscribeMe/Exception/UnsupportedUnsubscribePlatformException.php b/src/SubscribeMe/Exception/UnsupportedUnsubscribePlatformException.php new file mode 100644 index 0000000..3f3cbd0 --- /dev/null +++ b/src/SubscribeMe/Exception/UnsupportedUnsubscribePlatformException.php @@ -0,0 +1,13 @@ +getResponseBody()['message']); } } + + /** + * @inheritdoc + */ + public function unsubscribe(string $email): bool + { + try { + if (!is_string($this->getApiKey())) { + throw new ApiCredentialsException(); + } + + if (!is_string($this->getContactListId())) { + throw new CannotSubscribeException('Contact list id is required for subscribe'); + } + + $body = [ + 'emails' => [$email], + ]; + + $bodyStreamed = $this->getStreamFactory()->createStream(json_encode($body, JSON_THROW_ON_ERROR)); + + $uri = 'https://api.brevo.com/v3/contacts/lists/listId/contacts/remove'; + + $request = $this->getRequestFactory() + ->createRequest('POST', $uri) + ->withBody($bodyStreamed) + ->withAddedHeader('Content-Type', 'application/json') + ->withAddedHeader('api-key', $this->getApiKey()) + ->withAddedHeader('User-Agent', 'rezozero/subscribeme') + ; + + $res = $this->getClient()->sendRequest($request); + + // https://developers.sendinblue.com/reference/createcontact + if ($res->getStatusCode() === 200 || $res->getStatusCode() === 201) { + /** @var array $body */ + $body = json_decode($res->getBody()->getContents(), true); + if (isset($body['success'])) { + return true; + } + } + } catch (ClientExceptionInterface $exception) { + throw new CannotSubscribeException($exception->getMessage(), $exception); + } + + return false; + } } diff --git a/src/SubscribeMe/Subscriber/MailchimpSubscriber.php b/src/SubscribeMe/Subscriber/MailchimpSubscriber.php index 08f3a95..626c64b 100644 --- a/src/SubscribeMe/Subscriber/MailchimpSubscriber.php +++ b/src/SubscribeMe/Subscriber/MailchimpSubscriber.php @@ -9,6 +9,7 @@ use SubscribeMe\Exception\ApiResponseException; use SubscribeMe\Exception\CannotSendTransactionalEmailException; use SubscribeMe\Exception\CannotSubscribeException; +use SubscribeMe\Exception\UnsupportedUnsubscribePlatformException; use SubscribeMe\GDPR\UserConsent; use SubscribeMe\ValueObject\EmailAddress; @@ -64,6 +65,10 @@ public function subscribe(string $email, array $options, array $userConsents = [ throw new ApiCredentialsException(); } + if (!is_string($this->getContactListId())) { + throw new CannotSubscribeException('Contact list id is required for subscribe'); + } + $uri = 'https://' . $this->getDc() . '.api.mailchimp.com/3.0/lists/' . $this->getContactListId() . '/members'; $body = [ 'status' => $this->statusWhenSubscribed, @@ -213,4 +218,12 @@ private function transformVariables(array $variables): array } return $variables; } + + /** + * @inheritdoc + */ + public function unsubscribe(string $email): bool + { + throw new UnsupportedUnsubscribePlatformException(); + } } diff --git a/src/SubscribeMe/Subscriber/MailjetSubscriber.php b/src/SubscribeMe/Subscriber/MailjetSubscriber.php index 2e8ad45..01259c8 100644 --- a/src/SubscribeMe/Subscriber/MailjetSubscriber.php +++ b/src/SubscribeMe/Subscriber/MailjetSubscriber.php @@ -9,6 +9,7 @@ use SubscribeMe\Exception\ApiResponseException; use SubscribeMe\Exception\CannotSendTransactionalEmailException; use SubscribeMe\Exception\CannotSubscribeException; +use SubscribeMe\Exception\UnsupportedUnsubscribePlatformException; use SubscribeMe\GDPR\UserConsent; use SubscribeMe\ValueObject\EmailAddress; @@ -34,6 +35,10 @@ public function subscribe(string $email, array $options, array $userConsents = [ throw new ApiCredentialsException(); } + if (!is_string($this->getContactListId())) { + throw new CannotSubscribeException('Contact list id is required for subscribe'); + } + $name = null; if (isset($options['Name'])) { $name = $options['Name']; @@ -148,4 +153,12 @@ public function sendTransactionalEmail(array $emails, string|int $emailTemplateI throw new CannotsendTransactionalEmailException($exception->getResponseBody()['ErrorMessage']); } } + + /** + * @inheritdoc + */ + public function unsubscribe(string $email): bool + { + throw new UnsupportedUnsubscribePlatformException(); + } } diff --git a/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php b/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php index 7c1ba7b..bae1d7f 100644 --- a/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php +++ b/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php @@ -31,6 +31,10 @@ public function subscribe(string $email, array $options, array $userConsents = [ throw new ApiCredentialsException(); } + if (!is_string($this->getContactListId())) { + throw new CannotSubscribeException('Contact list id is required for subscribe'); + } + $mode = $options['mode'] ?? 'ignore'; unset($options['mode']); @@ -42,7 +46,7 @@ public function subscribe(string $email, array $options, array $userConsents = [ ]; $queryParams = http_build_query($body); - $uri = 'https://https://api.oximailing.com/lists/' . $this->getContactListId() . '/contacts?' . $queryParams; + $uri = 'https://api.oximailing.com/lists/' . $this->getContactListId() . '/contacts?' . $queryParams; try { $request = $this->getRequestFactory() ->createRequest('POST', $uri) @@ -73,4 +77,46 @@ public function sendTransactionalEmail(array $emails, string|int $emailTemplateI { throw new UnsupportedTransactionalEmailPlatformException(); } + + /** + * @inheritdoc + */ + public function unsubscribe(string $email): bool + { + if (!is_string($this->getApiKey())) { + throw new ApiCredentialsException(); + } + + if (!is_string($this->getApiSecret())) { + throw new ApiCredentialsException(); + } + + if (!is_string($this->getContactListId())) { + throw new CannotSubscribeException('Contact list id is required for subscribe'); + } + + $queryParams = http_build_query(['emails' => $email]); + + $uri = 'https://api.oximailing.com/lists/' . $this->getContactListId() . '/contacts?' . $queryParams; + try { + $request = $this->getRequestFactory() + ->createRequest('DELETE', $uri) + ->withAddedHeader('User-Agent', 'rezozero/subscribeme') + ->withAddedHeader('Authorization', 'Basic '.base64_encode(sprintf('%s:%s', $this->getApiKey(), $this->getApiSecret()))); + + $res = $this->getClient()->sendRequest($request); + + + if ($res->getStatusCode() === 200 || $res->getStatusCode() === 201) { + /** @var array $body */ + $body = json_decode($res->getBody()->getContents(), true); + if ($body['deleted'] == 1 || $body['not_found'] == 1) { + return true; + } + } + } catch (ClientExceptionInterface $exception) { + throw new CannotSubscribeException($exception->getMessage(), $exception); + } + return false; + } } diff --git a/src/SubscribeMe/Subscriber/SubscriberInterface.php b/src/SubscribeMe/Subscriber/SubscriberInterface.php index 1ab0918..cfcb5a2 100644 --- a/src/SubscribeMe/Subscriber/SubscriberInterface.php +++ b/src/SubscribeMe/Subscriber/SubscriberInterface.php @@ -5,6 +5,8 @@ namespace SubscribeMe\Subscriber; use JsonException; +use SubscribeMe\Exception\UnsupportedTransactionalEmailPlatformException; +use SubscribeMe\Exception\UnsupportedUnsubscribePlatformException; use SubscribeMe\GDPR\UserConsent; use SubscribeMe\ValueObject\EmailAddress; @@ -27,12 +29,19 @@ public function setContactListId(?string $contactListId): SubscriberInterface; */ public function subscribe(string $email, array $options, array $userConsents = []): bool|int; + /** + * @param string $email + * @return bool true on succeeded or false + * @throws JsonException|UnsupportedUnsubscribePlatformException + */ + public function unsubscribe(string $email): bool; + /** * @param array $emails * @param string|int $emailTemplateId * @param array> $variables * @return string Platform Response body after sending - * @throws JsonException + * @throws JsonException|UnsupportedTransactionalEmailPlatformException */ public function sendTransactionalEmail(array $emails, string|int $emailTemplateId, array $variables = []): string; } diff --git a/src/SubscribeMe/Subscriber/YmlpSubscriber.php b/src/SubscribeMe/Subscriber/YmlpSubscriber.php index ae61941..d5f9fac 100644 --- a/src/SubscribeMe/Subscriber/YmlpSubscriber.php +++ b/src/SubscribeMe/Subscriber/YmlpSubscriber.php @@ -8,6 +8,7 @@ use SubscribeMe\Exception\ApiCredentialsException; use SubscribeMe\Exception\CannotSubscribeException; use SubscribeMe\Exception\UnsupportedTransactionalEmailPlatformException; +use SubscribeMe\Exception\UnsupportedUnsubscribePlatformException; use SubscribeMe\GDPR\UserConsent; class YmlpSubscriber extends AbstractSubscriber @@ -47,6 +48,10 @@ public function subscribe(string $email, array $options, array $userConsents = [ throw new ApiCredentialsException(); } + if (!is_string($this->getContactListId())) { + throw new CannotSubscribeException('Contact list id is required for subscribe'); + } + $params = [ 'Key' => $this->getApiSecret(), 'Username' => $this->getApiKey(), @@ -124,6 +129,14 @@ public function subscribe(string $email, array $options, array $userConsents = [ return false; } + /** + * @inheritdoc + */ + public function unsubscribe(string $email): bool + { + throw new UnsupportedUnsubscribePlatformException(); + } + /** * @inheritdoc */ diff --git a/tests/MailjetMailerTest.php b/tests/MailjetMailerTest.php index 20b7f38..6e33ade 100644 --- a/tests/MailjetMailerTest.php +++ b/tests/MailjetMailerTest.php @@ -34,6 +34,7 @@ public function testSubscribe(): void $mailjetSubscriber->setApiKey('3f62c1f4-efb7-4bc7-b76d-0c2217d307b0'); $mailjetSubscriber->setApiSecret('df30148e-6cda-43ae-8665-9904f5f4f12a'); + $mailjetSubscriber->setContactListId('123'); $returnCode = $mailjetSubscriber->subscribe("passenger@mailjet.com", $options); $requests = $client->getRequests(); @@ -75,6 +76,7 @@ public function testSubscribeWithCodeError(): void $mailjetSubscriber->setApiKey('3f62c1f4-efb7-4bc7-b76d-0c2217d307b0'); $mailjetSubscriber->setApiSecret('df30148e-6cda-43ae-8665-9904f5f4f12a'); + $mailjetSubscriber->setContactListId('123'); $returnCode = $mailjetSubscriber->subscribe("passenger@mailjet.com", $options); $requests = $client->getRequests(); diff --git a/tests/OxiMailingMailerTest.php b/tests/OxiMailingMailerTest.php index 657a7e6..97b4b8c 100644 --- a/tests/OxiMailingMailerTest.php +++ b/tests/OxiMailingMailerTest.php @@ -10,6 +10,7 @@ use Nyholm\Psr7\Response; use PHPUnit\Framework\TestCase; use SubscribeMe\Exception\ApiCredentialsException; +use SubscribeMe\Exception\CannotSubscribeException; use SubscribeMe\Exception\UnsupportedTransactionalEmailPlatformException; use SubscribeMe\Subscriber\OxiMailingSubscriber; use SubscribeMe\ValueObject\EmailAddress; @@ -30,6 +31,7 @@ public function testSubscribe(): void $oxiMailingSubscriber->setApiKey('3f62c1f4-efb7-4bc7-b76d-0c2217d307b0'); $oxiMailingSubscriber->setApiSecret('df30148e-6cda-43ae-8665-9904f5f4f12a'); + $oxiMailingSubscriber->setContactListId('123'); $returnCode = $oxiMailingSubscriber->subscribe("tester@oximailing.com", ['mode' => 'ignored', 'firstName' => 'John', 'lastName' => 'Doe']); $requests = $client->getRequests(); @@ -39,7 +41,8 @@ public function testSubscribe(): void $this->assertEquals('Basic ' . base64_encode(sprintf('%s:%s', '3f62c1f4-efb7-4bc7-b76d-0c2217d307b0', 'df30148e-6cda-43ae-8665-9904f5f4f12a')), $requests[0]->getHeaders()['Authorization'][0]); $this->assertEquals('rezozero/subscribeme', $requests[0]->getHeaders()['User-Agent'][0]); $this->assertEquals('POST', $requests[0]->getMethod()); - $this->assertStringContainsString('api.oximailing.com/', $requests[0]->getUri()->getPath()); + $this->assertStringContainsString('api.oximailing.com', $requests[0]->getUri()->getHost()); + $this->assertStringContainsString('/lists/123/contacts', $requests[0]->getUri()->getPath()); } /** @@ -56,6 +59,7 @@ public function testSubscribeWithCodeError(): void $oxiMailingSubscriber->setApiKey('3f62c1f4-efb7-4bc7-b76d-0c2217d307b0'); $oxiMailingSubscriber->setApiSecret('df30148e-6cda-43ae-8665-9904f5f4f12a'); + $oxiMailingSubscriber->setContactListId('123'); $returnCode = $oxiMailingSubscriber->subscribe("tester@oximailing.com", ['mode' => 'ignored', 'firstName' => 'John', 'lastName' => 'Doe']); $requests = $client->getRequests(); @@ -65,7 +69,23 @@ public function testSubscribeWithCodeError(): void $this->assertEquals('Basic ' . base64_encode(sprintf('%s:%s', '3f62c1f4-efb7-4bc7-b76d-0c2217d307b0', 'df30148e-6cda-43ae-8665-9904f5f4f12a')), $requests[0]->getHeaders()['Authorization'][0]); $this->assertEquals('rezozero/subscribeme', $requests[0]->getHeaders()['User-Agent'][0]); $this->assertEquals('POST', $requests[0]->getMethod()); - $this->assertStringContainsString('api.oximailing.com/', $requests[0]->getUri()->getPath()); + $this->assertStringContainsString('api.oximailing.com', $requests[0]->getUri()->getHost()); + $this->assertStringContainsString('/lists/123/contacts', $requests[0]->getUri()->getPath()); + } + + /** + * @throws JsonException + */ + public function testExceptionListId(): void + { + $this->expectException(CannotSubscribeException::class); + $client = new Client(); + $factory = new Psr17Factory(); + $oxiMailingSubscriber = new OxiMailingSubscriber($client, $factory, $factory); + $oxiMailingSubscriber->setApiKey('3f62c1f4-efb7-4bc7-b76d-0c2217d307b0'); + $oxiMailingSubscriber->setApiSecret('df30148e-6cda-43ae-8665-9904f5f4f12a'); + $email = 'passenger1@mailjet.com'; + $oxiMailingSubscriber->unsubscribe($email); } /** @@ -95,4 +115,30 @@ public function testExceptionApiKey(): void $emails = 'passenger1@mailjet.com'; $oxiMailingSubscriber->subscribe($emails, ['mode' => 'update']); } + + /** @throws JsonException */ + public function testUnsubscribe(): void + { + $client = new Client(); + $factory = new Psr17Factory(); + + $client->setDefaultResponse(new Response(200, [], json_encode(['deleted' => 1, 'not_found' => 0], JSON_THROW_ON_ERROR))); + + $oxiMailingSubscriber = new OxiMailingSubscriber($client, $factory, $factory); + + $oxiMailingSubscriber->setApiKey('3f62c1f4-efb7-4bc7-b76d-0c2217d307b0'); + $oxiMailingSubscriber->setApiSecret('df30148e-6cda-43ae-8665-9904f5f4f12a'); + $oxiMailingSubscriber->setContactListId('123'); + $returnCode = $oxiMailingSubscriber->unsubscribe("tester@oximailing.com"); + + $requests = $client->getRequests(); + + $this->assertTrue($returnCode); + $this->assertCount(1, $requests); + $this->assertEquals('Basic ' . base64_encode(sprintf('%s:%s', '3f62c1f4-efb7-4bc7-b76d-0c2217d307b0', 'df30148e-6cda-43ae-8665-9904f5f4f12a')), $requests[0]->getHeaders()['Authorization'][0]); + $this->assertEquals('rezozero/subscribeme', $requests[0]->getHeaders()['User-Agent'][0]); + $this->assertEquals('DELETE', $requests[0]->getMethod()); + $this->assertStringContainsString('api.oximailing.com', $requests[0]->getUri()->getHost()); + $this->assertStringContainsString('/lists/123/contacts', $requests[0]->getUri()->getPath()); + } } diff --git a/tests/YmlpMailerTest.php b/tests/YmlpMailerTest.php index b7ab713..1e431a6 100644 --- a/tests/YmlpMailerTest.php +++ b/tests/YmlpMailerTest.php @@ -30,6 +30,7 @@ public function testSubscribe(): void $ymlpSubscriber->setApiKey('3f62c1f4-efb7-4bc7-b76d-0c2217d307b0'); $ymlpSubscriber->setApiSecret('df30148e-6cda-43ae-8665-9904f5f4f12a'); + $ymlpSubscriber->setContactListId('123'); $returnCode = $ymlpSubscriber->subscribe("jdoe@example.com", []); $requests = $client->getRequests(); @@ -39,7 +40,7 @@ public function testSubscribe(): void 'Username' => $ymlpSubscriber->getApiKey(), 'OverruleUnsubscribedBounced' => 0, 'Email' => 'jdoe@example.com', - 'GroupID' => 0, + 'GroupID' => 123, 'Output' => 'JSON' ]; $body = http_build_query($body, '', '&'); @@ -73,11 +74,12 @@ public function testSubscribeWithoutCode(): void $ymlpSubscriber->setApiKey('3f62c1f4-efb7-4bc7-b76d-0c2217d307b0'); $ymlpSubscriber->setApiSecret('df30148e-6cda-43ae-8665-9904f5f4f12a'); + $ymlpSubscriber->setContactListId('123'); $returnCode = $ymlpSubscriber->subscribe("jdoe@example.com", []); $requests = $client->getRequests(); - $body = "Key=df30148e-6cda-43ae-8665-9904f5f4f12a&Username=3f62c1f4-efb7-4bc7-b76d-0c2217d307b0&OverruleUnsubscribedBounced=0&Email=jdoe%40example.com&GroupID=0&Output=JSON"; + $body = "Key=df30148e-6cda-43ae-8665-9904f5f4f12a&Username=3f62c1f4-efb7-4bc7-b76d-0c2217d307b0&OverruleUnsubscribedBounced=0&Email=jdoe%40example.com&GroupID=123&Output=JSON"; $this->assertTrue($returnCode); $this->assertCount(1, $requests); From 9d05814769d7e5a01abefe448232435639bc316a Mon Sep 17 00:00:00 2001 From: eliot lauger Date: Tue, 21 Jan 2025 10:09:12 +0100 Subject: [PATCH 4/6] feat: Update README.md with unsubscribe --- README.md | 41 ++++++++++++++++++- .../Brevo/Remove a contacts from a list.bru | 25 +++++++++++ .../Subscriber/BrevoSubscriber.php | 2 +- 3 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 bruno/subscribeme/Brevo/Remove a contacts from a list.bru diff --git a/README.md b/README.md index 9ec33b4..7c701ca 100644 --- a/README.md +++ b/README.md @@ -206,6 +206,10 @@ $variables = [ $subscriber->sendTransactionalEmail($emails, $emailTemplateId, $variables); ``` +### Mailchimp options unsubscribing + +Mailchimp does not support unsubscribing a user from a list with their email address, we throw an `UnsupportedUnsubscribePlatformException`. + ## YMLP ### YMLP options subscriber @@ -228,6 +232,10 @@ For getting your additional fields ID: see https://www.ymlp.com/api/Fields.GetLi YMLP does not support transactional email, we throw an `UnsupportedTransactionalEmailPlatformException`. +### YMLP options unsubscribing + +YMLP does not support unsubscribing a user from a list with their email address, we throw an `UnsupportedUnsubscribePlatformException`. + ## Brevo ### Brevo subscriber options @@ -286,6 +294,19 @@ $variables = [ $subscriber->sendTransactionalEmail($emails, $templateEmail, $variables); ``` +### Brevo options unsubscribing + +See https://developers.brevo.com/reference/removecontactfromlist + +```php +$subscriber = $factory->createFor('brevo'); +// Brevo only requires an API Key +$subscriber->setApiKey('brevo_api_key'); +// Use Just One list Id in same time for remove contact from a list +$subscriber->setContactListId('3'); +$subscriber->unsubscribe('jimmy98@example.com'); +``` + ## Mailjet ### Mailjet subscriber options @@ -324,6 +345,10 @@ $variables = [ $subscriber->sendTransactionalEmail($emails, $templateEmail, $variables); ``` +### MailJet options unsubscribing + +MailJet does not support unsubscribing a user from a list with their email address, we throw an `UnsupportedUnsubscribePlatformException`. + ## OxiMailing @@ -346,4 +371,18 @@ $subscriber->subscribe('hello@super.test', ['mode' => 'update']); ### OxiMailing sender transactional email options -OxiMailing does not support transactional email, we throw an `UnsupportedTransactionalEmailPlatformException`. \ No newline at end of file +OxiMailing does not support transactional email, we throw an `UnsupportedTransactionalEmailPlatformException`. + +### OxiMailing options unsubscribing + +See https://api.oximailing.com/doc/#/contacts/delete_lists__ListId__contacts + +```php +$subscriber = $factory->createFor('oximailing'); +// OxiMailing requires an API Key and an API Secret +$subscriber->setApiKey('oximailing_api_key'); +$subscriber->setApiSecret('oximailing_api_secret') +// You can only unsubscribe one user to one list +$subscriber->setContactListId('123'); +$subscriber->unsubscribe('jimmy98@example.com'); +``` \ No newline at end of file diff --git a/bruno/subscribeme/Brevo/Remove a contacts from a list.bru b/bruno/subscribeme/Brevo/Remove a contacts from a list.bru new file mode 100644 index 0000000..715fe19 --- /dev/null +++ b/bruno/subscribeme/Brevo/Remove a contacts from a list.bru @@ -0,0 +1,25 @@ +meta { + name: Remove a contacts from a list + type: http + seq: 4 +} + +post { + url: {{brevo_base_url}}/v3/contacts/lists/:listId/contacts/remove + body: json + auth: none +} + +params:path { + listId: +} + +headers { + content-type: application/json +} + +body:json { + { + "emails": "john.smith@contact.com" + } +} diff --git a/src/SubscribeMe/Subscriber/BrevoSubscriber.php b/src/SubscribeMe/Subscriber/BrevoSubscriber.php index 2664ccb..4cb5c59 100644 --- a/src/SubscribeMe/Subscriber/BrevoSubscriber.php +++ b/src/SubscribeMe/Subscriber/BrevoSubscriber.php @@ -217,7 +217,7 @@ public function unsubscribe(string $email): bool $bodyStreamed = $this->getStreamFactory()->createStream(json_encode($body, JSON_THROW_ON_ERROR)); - $uri = 'https://api.brevo.com/v3/contacts/lists/listId/contacts/remove'; + $uri = 'https://api.brevo.com/v3/contacts/lists/'.$this->getContactListId().'/contacts/remove'; $request = $this->getRequestFactory() ->createRequest('POST', $uri) From dad86c9c877a8874f1a1a6a437f6d87733b74bfb Mon Sep 17 00:00:00 2001 From: eliot lauger Date: Tue, 21 Jan 2025 13:51:33 +0100 Subject: [PATCH 5/6] chore: fix threads --- .../Exception/UnsupportedUnsubscribePlatformException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SubscribeMe/Exception/UnsupportedUnsubscribePlatformException.php b/src/SubscribeMe/Exception/UnsupportedUnsubscribePlatformException.php index 3f3cbd0..d6a5981 100644 --- a/src/SubscribeMe/Exception/UnsupportedUnsubscribePlatformException.php +++ b/src/SubscribeMe/Exception/UnsupportedUnsubscribePlatformException.php @@ -8,6 +8,6 @@ final class UnsupportedUnsubscribePlatformException extends \LogicException { public function __construct() { - parent::__construct('The platform does not have an unsubscribe endpoint', 0); + parent::__construct('The platform does not support unsubscribing email addresses.', 0); } } From c09fb7d4419e746e84e54cb651af2473f162b339 Mon Sep 17 00:00:00 2001 From: eliot lauger Date: Tue, 21 Jan 2025 14:09:09 +0100 Subject: [PATCH 6/6] chore: fix threads --- README.md | 3 +++ src/SubscribeMe/Subscriber/OxiMailingSubscriber.php | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7c701ca..34b5546 100644 --- a/README.md +++ b/README.md @@ -366,6 +366,9 @@ $subscriber->setContactListId('123'); //- ignore : remove duplicates //- insert : don't do anything (all contacts are imported even duplicates) //- update : update existing contacts information rather than adding duplicates +// Defaults to mode: ignore +$subscriber->subscribe('hello@super.test'); +// You can override subscription mode using options array $subscriber->subscribe('hello@super.test', ['mode' => 'update']); ``` diff --git a/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php b/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php index bae1d7f..1623e15 100644 --- a/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php +++ b/src/SubscribeMe/Subscriber/OxiMailingSubscriber.php @@ -36,7 +36,9 @@ public function subscribe(string $email, array $options, array $userConsents = [ } $mode = $options['mode'] ?? 'ignore'; - unset($options['mode']); + if (isset($options['mode'])) { + unset($options['mode']); + } $contacts[$email] = $options;