Use PDF mediaId in Email Attachment - shopware

I am using the "api/_action/mail-template/send" action in a Shopware App to send an email to the customer and I want to add a document which was created before. If I understand it correct, I only have to give the mediaId as parameter to the request. The problem is, that the file can not be found ('detail' => 'File not found at path: media/29/50/18/1660063117/invoice_2544.pdf'),because the PDF files are in the files directory.
What am I doing wrong? Are PDFs a special case?
Thanks
Danny

Unfortunately you are correct with your assumption. The primary keys you provide with mediaIds may only refer to files stored in the public filesystem. There's already a feature flag in place that will allow to provide documentIds specifically instead, but since you're developing an app I doubt you'll be able to activate said feature flag for this purpose and the feature flag is only to be removed with the upcoming major release 6.5.0.0.
What you can do however is provide raw binary data for your attachments with the request payload key binAttachments. You can find how this data is handled in the MailFactory service.
There's the endpoint POST /api/_action/order/document/download you can request with a payload documentIds that will give you the files binary content (You essentially download the file). You can provide multiple ids but it will merge all documents into one, so you might want to do one request per document if you want to keep the files separated as attachments.
You can then use the binary data you retrieved as content of your binAttachments.
PHP example with GuzzleHttp\Client:
$response = $client->post($host . '/api/_action/order/document/download', [
'json' => [
'documentIds' => ['b0b3ac0ca218473babaffc7f0a800f36'],
],
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $accessToken,
],
]);
$client->post($host . '/api/_action/mail-template/send', [
'form_params' => [
'recipients' => [
'test1#example.com' => 'Test user 1',
],
'salesChannelId' => '6923fd0d41e04521a2a259ba13f20bc3',
'contentHtml' => 'officia Lorem non aute',
'contentPlain' => 'eu veniam m',
'subject' => 'sed adipisicing',
'senderName' => 'enim occaecat aliquip veniam',
'senderEmail' => 'test#example.com',
'binAttachments' => [
[
'content' => $response->getBody()->getContents(),
'fileName' => 'test.pdf',
'mimeType' => 'application/pdf',
],
],
],
'headers' => [
'Authorization' => 'Bearer ' . $accessToken,
],
]);
While this is certainly a workaround, as of now this might be your best bet to achieve this purely with an app.

Related

Is there a way to query entries and categories together in Craft 3?

'm working on a global site search ( autocomplete ), right now it have almost all entries ( that have uri defined ) for results, it uses js fetch and Craft element api in a json file that filter results based on a query string. Works great!
I was wondering if it's possible to add to the query categories too, i don't know if this is possible buy maybe it's a way to merge them, here is my json api code, thanks!
'search.json' => function() {
$qParam = Craft::$app->request->getParam('q');
return [
'elementType' => Entry::class,
'paginate' => false,
'criteria' => [
'uri'=> ':notempty:',
'search' => $qParam,
'orderBy' => 'score',
'limit' => 20,
],
'cache' => false,
'transformer' => function(Entry $item) {
return [
'title' => $item->title,
'url' => $item->url,
];
},
];
}

How to capture funds later when using Stripe's Checkout feature?

I am using Stripe's Checkout feature. My product takes a few minutes to generate, so I want to place a hold on the funds, only charging the customer once their product is ready. Is it possible to do so without refactoring away from using this feature?
This code works for me:
<?php
$amount = 50;
$stripe = new \Stripe\StripeClient(env('STRIPE_SECRET'));
$session = $stripe->checkout->sessions->create([
'payment_method_types' => ['card'],
'success_url' => 'reservation.success'.'?id={CHECKOUT_SESSION_ID}',
'cancel_url' => 'reservation.cancel',
'payment_intent_data' => [
'capture_method' => 'manual',
],
'line_items' => [[
'price_data' => [
'currency' => "eur",
'product_data'=> [
'name'=> "My awesome product",
],
'unit_amount'=> $amount * 100,
],
'quantity' => 1
]],
'mode' => 'payment',
]);
return redirect()->to($session->url);
This was done in laravel. You might need to adapt it a little to your needs.
Of course you need to
adapt $amount to the amount you want to collect in €.
change the currency if you need to
set the route for success_url
set the route for cancel_url
After calling this code the user is redirected to the session url, to make the payment, which is then reserved on his card.
You can then capture the payment later via:
<?php
$stripe = new \Stripe\StripeClient(env('STRIPE_SECRET'));
$session_id = 'THE_CHECKOUT_SESSION_ID';
$payment_intent = $stripe->checkout->sessions->retrieve($session_id)-payment_intent;
$intent = \Stripe\PaymentIntent::retrieve($payment_intent);
// Capture all
$intent->capture();
// Alternative: Capture only a part of the reserved amount
// $intent->capture(['amount_to_capture' => 750]);
See also https://stripe.com/docs/payments/capture-later

Salesforce Rest API - Inserting values into Lookup fields

Inserting 200 records at a time (JSON array) into a custom object using the Salesforce REST API. Example with one record:
{
"records": [
{
"attributes" : {"type" : "Timecard__c"},
"Project__c": "a9S1F0000004LHDUA2",
"Milestone__c": "a9F1F00000007GOUAY",
"Resource__c": "0031F00000TApKqQAL",
"Date__c": "2020-08-16",
"Hours__c": 7,
"Notes__c": "Did some work"
},
]
}
The first three fields are lookups to other objects. The data I'm given to insert has names for the lookup fields (eg Project__c = "Canoe reconstruction", Milestone__c = "Rebuild gunwales", Resource__c = "John Smith".
My current plan is to generate arrays of Projects, Milestones, and Resources containing the Ids and Names then patch the JSON I have to load.
Does the Salesforce REST API offer a way to set the values of the Lookups to the text name such that it would find the Id on its own or is my current approach the most efficient way to handle this?
Here's the code I'm using for the processed data load...
const submitTimecards = async() => {
const token = await getAccessToken()
const data = JSON.parse(fs.readFileSync('timecards.json', 'utf-8'))
const response = await axios({
method: 'post',
url: `${salesforceUrl}/composite/sobjects`,
data,
headers: {
'Authorization': `OAuth ${token}`,
'Content-Type': 'application/json'
}
})
return response
}
By Name it's bit tricky. SF "natural" way would be to specify a helper field marked as external id (ideally it'd be marked unique too) and then you can use your references. "Dear Salesforce, I don't care what's your internal primary key of that Account record I need to link to, on my end it's 12345, go do your magic, look it up yourself".
It's in https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_upsert.htm?search_text=patch, look for example that says "Upserting Records and Associating with an External ID". It might not be very clear but if you have SF admin in team he/she should know what can be done with "upsert" operation in Data Loader, same principles would apply. I have an example that upserts multiple objects in one go, it'll be bit too crazy but try to read it: https://salesforce.stackexchange.com/a/274696/799
Or you could batch multiple requests into one all-or-none API call. It'll be like series of instructions to SF, not multiple round trips to you and having to cache results somewhere. In that call you could run queries and then use their temporary results in your final request. It'll look bit like https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_composite_record_manipulation.htm or https://developer.salesforce.com/blogs/tech-pubs/2017/01/simplify-your-api-code-with-new-composite-resources.html (scroll to the "A Simple Example, Now Using Composite!" part)
{
"compositeRequest" : [{
"method" : "POST",
"url" : "/services/data/v38.0/sobjects/Account",
"referenceId" : "refAccount",
"body" : {
"Name" : "My New Account"
}
},{
"method" : "GET",
"url" : "/services/data/v38.0/query/?q=select+id+from+contact+where+name='Howard+Jones'",
"referenceId" : "refContact"
},{
"method" : "PATCH",
"url" : "/services/data/v38.0/sobjects/Contact/#{refContact.records[0].Id}",
"referenceId" : "refContactUpdated",
"body" : {
"AccountId" : "#{refAccount.id}"
}
}]
}
The downside is that with composite you won't be able to do all 200 in 1 go.
You can have up to 25 subrequests in a single call. Up to 5 of these
subrequests can be sObject Collections or query operations, including
Query and QueryAll requests.

Select document file from phone and upload to server

I am trying to get a document file from phone storage when button is pressed and than upload it server. But i don't know which library to use and how to do it.
If you are willing to use a library you have both React-native-fetch-blob or axios.
If React-native-fetch-blob you can do it like this:
RNFetchBlob.fetch('POST', 'http://www.example.com/upload-form', {
Authorization : "Bearer access-token",
otherHeader : "foo",
'Content-Type' : 'multipart/form-data',
}, [
// element with property `filename` will be transformed into `file` in form data
{ name : 'avatar', filename : 'avatar.png', data: binaryDataInBase64},
// custom content type
{ name : 'avatar-png', filename : 'avatar-png.png', type:'image/png', data: binaryDataInBase64},
// part file from storage
{ name : 'avatar-foo', filename : 'avatar-foo.png', type:'image/foo', data: RNFetchBlob.wrap(path_to_a_file)},
// elements without property `filename` will be sent as plain text
{ name : 'name', data : 'user'},
{ name : 'info', data : JSON.stringify({
mail : 'example#example.com',
tel : '12345678'
})},
]).then((resp) => {
// ...
}).catch((err) => {
// ...
})
You will hae to manage to get the file path from libraries like RNFS or even RNFetch blob.
https://github.com/wkh237/react-native-fetch-blob
You can use axios too (https://github.com/mzabriskie/axios), but I don't use it so I can't help you any further.
The difference between both is the way they send the data. RNFB uses the fetch api and goes down to native to get the Base64 encoding, axios works over XMLHttpRequests, which is more likely to be used in internet browsers.
Hope it helps.

Omnipay Stripe Extra Parameters

Im trying to use the Omnipay API with Stripe, but I can't pass in extra parameters such as "Name", "Metadata", or "Zip".
// The token is grabbed from stripe JS
$this->stripe_gateway = Omnipay::create('Stripe');
$response = $this->stripe_gateway->purchase([
'amount' => $amount,
'currency' => 'usd',
'name' => $name,
'description' => $product->title,
'zip_address' => $zip,
'metadata' => [
'name' => $name,
'user_id' => $this->session->get('id')
],
'token' => $stripeToken,
])->send();
I cant get anything to work, is this not built into the API?
Omnipay uses it's own parameter names, not Stripe's. That's because Omnipay tries to abstract most of the differences between the various payment gateways.
Right now, the omnipay/stripe package doesn't support sending some of those parameters (only amount, currency, description, and now metadata). You can see the supported parameters here:
https://github.com/omnipay/stripe/blob/master/src/Message/AuthorizeRequest.php
That said, you can still easily access the underlying Stripe request to add your own custom parameters:
$request = $this->stripe_gateway->purchase([
'amount' => $amount,
'token' => $stripeToken,
'metadata' => ['foo' => 'bar'],
]);
$data = $request->getData();
$data['zip_address'] = '12345';
$data['another_custom_parameter'] = 'wow';
$response = $request->sendData($data);
Note that:
$data = $request->getData();
$response = $request->sendData($data);
is exactly the same as calling:
$response = $request->send();
Alternatively, you could create a pull request to add extra parameters to the Omnipay Stripe package. I just added the metadata parameter as an example of this:
https://github.com/omnipay/stripe/commit/99c82dc42c7c0b9ec58d8c4fb917f3dc5d1c23e2

Resources