Golang Example
This page will demonstrate how we implemented Lagom on a Golang server. We will show you how to add a payment link on an article snippet page, and how to verify the payment on the server side. If a payment is successful, we will return the complete page.
Current infrastructure
The full code of this demo is available on this github repository.
To simplify this example, our server currently only serves a single article snippet webpage.
http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "./article_snippet.html")
}))
http.ListenAndServe(":12345", nil)
And this snippet webpage is a single HTML file.
<!DOCTYPE html>
<html>
<h1>This is an article snippet</h1>
</html>
Webpage change
We now need to include the Lagom payment link on the snippet page. This link points to the Lagom payout page, and it contains 3 elements:
<h1>This is an article snippet</h1>
<a href="#" onclick="(function a(event) { location.href = 'https://lagom.org/payout?amount=100&provider=test-provider&cb=' + btoa(location.href) })()">
This is a paid article, click here to read with Lagom
</a>
- the amount to pay for this page, here 1.00 EUR (the currency is defined by the provider)
- the provider ID, to identify the recipient, here
test-provider
- the page to pay for (in base64), here using an inlined function to dynamically set the URL
The provider
test-provider
will automatically log you in with a testing account upon payment, where no funds are moved. Feel free to use this provider for testing.
In the same time, we can also create the full article page, which will be served if the payment is successful.
<!DOCTYPE html>
<html>
<h1>This is the full article</h1>
<i>thank you for purchasing this article !</i>
</html>
Server modification
We will now implement the Lagom verification step. The verification is done in the lagomVerify
function, and if successful we will return the full article.
http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
err := lagomVerify(r, "/", 100)
if err != nil {
http.ServeFile(w, r, "./article_snippet.html")
} else {
http.ServeFile(w, r, "./article.html")
}
}))
The lagomVerify
function is defined below. This function will ensure that:
- a payment was committed, and is valid for the matching amount
- the payment applies to this page only
- the payment was performed in the last 10 seconds
The secret defined in this snippet matches the provider
provider-test
.
const SECRET = "04b3623a9f7c553c272e3d3def949e3ac781ff8145ee87f22defc7616dae3f86a165547706f5e381a4d70070b234109fdd8daf80167e673ceda05503eb0d3123"
func lagomVerify(req *http.Request, page string, amount int) error {
// extract callback params from URL, decode and parse
lguid := req.URL.Query().Get("lguid")
lgts := req.URL.Query().Get("lgts")
lgsig := req.URL.Query().Get("lgsig")
lgid := req.URL.Query().Get("lgid")
lgamt := req.URL.Query().Get("lgamt")
// verify timestamp is within 10 seconds
ts, err := strconv.Atoi(lgts)
if err != nil || int(time.Now().UTC().Unix()) > ts+10 {
return fmt.Errorf("This link has expired")
}
// check amount and page
lgamtint, err := strconv.Atoi(lgamt)
if err != nil || lgamtint != amount {
return fmt.Errorf("This link is not valid")
}
// verify signature with pre shared secret
mac := hmac.New(sha256.New, []byte(SECRET))
mac.Write([]byte(lguid + lgid + lgts + page + lgamt))
ret := mac.Sum(nil)
if lgsig != fmt.Sprintf("%x", ret) {
return fmt.Errorf("This link is not valid")
}
return nil
}
We can now run the example again and follow the payment stage. Congratulations, you implemented your first Lagom payment page !