Hugo: How to create PayPal partial
Overview
Acknowledgements
One should be aware that one's site will require JavaScript to function correctly. Further this guide requires some knowledge in creating templates in the layouts
directory. This approach uses the power of single content pieces within a directory to populate a page. Thus one might need to adjust layouts/_default/single.html
to get the desired outcome.
This guide has been written as a result to developing the store at the Munich Rucking Crew website1.
Goal
The goal is to create a drop-down with respective image and item name change. When clicking on Add to Cart one will be redirected to PayPal with the selected item already added to the cart.
The live example can be found on the Munich Rucking Crew's merch page.
Requirements
One will need all the variations of a specific item and its name and image. Additionally, one will need to generate the PayPal HTML code, which looks like
1<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
2 <input type="hidden" name="cmd" value="_s-xclick" />
3 <input type="hidden" name="hosted_button_id" value="<PAYPAL_TOKEN>" />
4 <table>
5 <tr>
6 <td>
7 <input type="hidden" name="<PAYPAL_INIT_REFERENCE>" value="<PAYPAL_INIT_VALUE>"/>
8 <PAYPAL_INIT_NAME>
9 </td>
10 </tr>
11 <tr>
12 <td>
13 <select name="os0">
14 <option value="<PAYPAL_ITEM_1_VALUE>"><PAYPAL_ITEM_1_NAME></option>
15 <option value="<PAYPAL_ITEM_2_VALUE>"><PAYPAL_ITEM_2_NAME></option>
16 </select>
17 </td>
18 </tr>
19 </table>
20 <input type="hidden" name="currency_code" value="EUR" />
21 <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_cart_LG.gif" border="0" name="submit" title="PayPal - The safer, easier way to pay online!" alt="Add to Cart" />
22</form>
with
<PAYPAL_TOKEN>
is the main PayPal hook, so it knows how to update the store relative to all further actions taken within the form<PAYPAL_INIT_REFERENCE>
usually ason0
<PAYPAL_INIT_VALUE>
is usually equivalent to<PAYPAL_ITEM_1_VALUE>
<PAYPAL_INIT_NAME>
is usually equivalent to<PAYPAL_ITEM_1_NAME>
<PAYPAL_ITEM_1_NAME>
is usually equal to<PAYPAL_ITEM_1_VALUE>
<PAYPAL_ITEM_2_NAME>
is usually equal to<PAYPAL_ITEM_2_VALUE>
Setup
One will first create the paypal-button.html
partial template2, then the list-store.html
partial template, and finally the two items to trigger the paypal-button partial template.
Creating the paypal-button partial template
- Create a new file
layouts/partials/paypal-button.html
. - Add this code
1{{ $output := "" }}
2{{ $uniqueID := .uniqueID }}
3{{ $output = print $output (printf "<div class=\"paypal-button-%s\">" $uniqueID) }}
4{{ $output = print $output "<form action=\"https://www.paypal.com/cgi-bin/webscr\" method=\"post\" target=\"_blank\">" }}
5{{ $output = print $output "<input type=\"hidden\" name=\"cmd\" value=\"_s-xclick\" />" }}
6{{ $output = print $output (printf "<input type=\"hidden\" name=\"hosted_button_id\" value=\"%s\" />" .paypal) }}
7{{ if .items }}
8 {{ $output = print $output "<table>" }}
9 {{ $output = print $output ( printf "<tr><td><input type=\"hidden\" name=\"on0\" value=\"%s\"></td></tr>" (index .items 0).value )}}
10 {{ $output = print $output "<tr>" }}
11 {{ $output = print $output "<td class=\"paypal-cell\">" }}
12 {{ $output = print $output "<span>Choose:</span>" }}
13 {{ $output = print $output (printf "<select id=\"item-select-%s\" class=\"paypal-select\" name=\"os0\">" $uniqueID) }}
14 {{ range .items }}
15 {{ $output = print $output (printf "<option value=\"%s\" data-image=\"%s\">%s</option>" .value .image .name) }}
16 {{ end }}
17 {{ $output = print $output (printf "</select></td></tr></table>") }}
18 {{ $selectedValue := index .items 0 }}
19 {{ $output = print $output (printf `<script>
20 document.getElementById("item-select-%s").addEventListener("change", function() {
21 var selectedValue = this.value;
22 var selectedOption = this.options[this.selectedIndex];
23 var selectedImage = selectedOption.getAttribute("data-image");
24 var selectedItemContainer = document.getElementById("selected-item-%s");
25 selectedItemContainer.querySelector(".selected-value").innerText = selectedValue;
26 selectedItemContainer.querySelector(".selected-image").src = "/" + selectedImage;
27 });
28 document.getElementById("selected-item-%s").querySelector(".selected-value").innerText = "%s";
29 document.getElementById("selected-item-%s").querySelector(".selected-image").src = "/%s";
30 </script>` $uniqueID $uniqueID $uniqueID $selectedValue.value $uniqueID $selectedValue.image | safeJS) }}
31{{ end }}
32{{ $output = print $output "<input type=\"hidden\" name=\"currency_code\" value=\"EUR\" />" }}
33{{ $output = print $output "<input type=\"image\" src=\"https://www.paypalobjects.com/en_US/i/btn/btn_cart_LG.gif\" border=\"0\" name=\"submit\" title=\"PayPal - The safer, easier way to pay online!\" alt=\"Add to Cart\" />" }}
34{{ $output = print $output "</form>" }}
35{{ $output = print $output "</div>" }}
36{{ return $output }}
- Create the file
layouts/partials/list-store.html
. - Add this code within the tag that holds the
id="main"
(assuming the theme uses this approach).
1<!-- theme hugo header plus beginning of body -->
2
3<ul class="grid-list" style="list-style: none;">
4 {{ range .Pages }}
5 {{ if not .Params.draft}}
6 <li>
7 <h3>{{.Title}}</h3>
8 {{ $uniqueID := .File.UniqueID }}
9 {{ if not .Params.paypalItems }}
10 <img src="/{{.Params.image}}"/>
11 {{ end }}
12 {{ if .Params.paypalItems }}
13 <div id="selected-item-{{ $uniqueID }}">
14 <span class="selected-value">{{ (index .Params.paypalItems 0).value }}</span>
15 <img class="selected-image" src="/{{ (index .Params.paypalItems 0).image }}" />
16 </div> <!-- Default to the first item's value -->
17 {{ end }}
18 <p>{{.Params.description}}</p>
19 <div>{{.Content}}</div>
20 <p>{{.Params.price}} € per item</p>
21 {{ $itemsOutput := partial "paypal-button" (dict "paypal" .Params.paypal "items" .Params.paypalItems "headName" .Params.paypalHeadName "headValue" .Params.paypalHeadValue "uniqueID" $uniqueID) }}
22 {{ if $itemsOutput }}
23 <div class="items-container">
24 {{ $itemsOutput | safeHTML }}
25 </div>
26 {{ end }}
27 <p class="paypal-hint">(*) You will be redirected to PayPal and can purchase more items at once.</p>
28 </li>
29 {{ end }}
30 {{ end }}
31</ul>
32
33<!-- theme hugo footer -->
The code should also work within generic HTML5 template.
- Connect the
layouts/_default/list.html
to reference the newly createdlist-style.html
file. One option is to use the listType approach. - Add the styles to the file based off of the theme used.
1.grid-list {
2 display: grid;
3 grid-template-columns: repeat(3, 1fr);
4 grid-gap: 10px;
5 padding: 0;
6 margin: 0;
7 list-style: none;
8}
9
10.grid-list li {
11 background-color: #f0f0f0;
12 padding: 10px;
13 text-align: center;
14}
15
16.grid-list p {
17 margin: 0 0 1rem 0;
18}
19
20/* Styles for the PayPal button */
21[class^="paypal-button-"] {
22 display: flex;
23 justify-content: left;
24 align-items: center;
25}
26
27[class^="paypal-button-"] form {
28 width: 100%;
29 margin: 0 0 1rem 0;
30}
31
32[class^="paypal-button-"] .paypal-select {
33 background-image: none;
34 border: 1px solid #3396c5;
35 cursor: pointer;
36}
37
38[class^="paypal-button-"] .paypal-cell {
39 text-align: left;
40}
41
42.paypal-hint {
43 font-size: 0.6rem;
44}
- Create the files
content/post/store/item-1.md
andcontent/post/store/item-2.md
. - Each Markdown file should have its meta data like
1+++
2title = 'Item: Name'
3slug = 'item-name'
4image = 'path/to/image/1.webp'
5description = 'item description'
6disableComments = true
7
8paypal = '<PAYPAL_TOKEN>'
9price = '<your_price>'
10
11paypalHeadName = '<PAYPAL_INIT_REFERENCE>'
12paypalHeadValue = '<PAYPAL_INIT_VALUE>'
13
14[[paypalItems]]
15name = '<PAYPAL_ITEM_1_NAME>'
16value = '<PAYPAL_ITEM_1_VALUE>'
17image = 'path/to/image/1.webp'
18
19[[paypalItems]]
20name = '<PAYPAL_ITEM_2_NAME>'
21value = '<PAYPAL_ITEM_2_VALUE>'
22image = 'path/to/image/2.webp'
23+++
24
25<your_description_of_the_item>
where all values should be adjusted accordingly. The first 5 parameters are based off of the theme in use, so adjusted if needed.
- The file tree looks like
1content/
2|_ post/
3 |_ store/
4 |_ item-1.md
5 |_ item-2.md
6
7layouts/
8|_ partials/
9 |_ paypal-button.html
10 |_ list-store.html
Details
Approach listType
Hugo permits multiple ways to assign what list type should be reference relative to the directory. One approach is to use the listType
parameter within the _index.md
file.
This means if one looks back at the file tree, one would create the content/post/store/_index.md
file.
1content/
2|_ post/
3 |_ store/
4 |_ _index.md
5 |_ item-1.md
6 |_ item-2.md
7
8layouts/
9|_ partials/
10 |_ paypal-button.html
11 |_ list-store.html
This index file holds all the meta data relevant for the subdirectory store. It looks like
1+++
2title = "Store"
3listType = "list-store.html"
4+++
5
6Welcome to our store!
Further one will need to update the layouts/_default/list.html
file to use the listType approach. The new file looks like
1{{ if .Params.listType }}
2 {{ partial .Params.listType . }}
3{{ end }}
The final file tree looks like
1content/
2|_ post/
3 |_ store/
4 |_ _index.md
5 |_ item-1.md
6 |_ item-2.md
7
8layouts/
9|_ _default/
10| |_ list.html
11|
12|_ partials/
13 |_ paypal-button.html
14 |_ list-store.html
Code explanation
For a more in depth explanation of how this code has come to be, one can read through the Qoto Mastodon thread3. Only regard the posts that start with #DailyBloggingChallenge
. The other ones are commentary on the thread.
Got Comments !?
By default bf5.eu
has comments deactivated. Nonetheless, this post has a Qoto Mastodon thread3, where comments can be made.
References
-
Munich Rucking Crew's website store, Development, Production ↩︎