※ Lagom

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>
  1. the amount to pay for this page, here 1.00 EUR (the currency is defined by the provider)
  2. the provider ID, to identify the recipient, here test-provider
  3. 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:

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 !