---
# System prepended metadata

title: Heroku Webhook HMAC-SHA256
tags: [heroku]

---

# Heroku Webhook HMAC-SHA256

[TOC]

###### tags: `heroku`

---

## Config Vars

```java
	private static final String HEROKU_API_TOKEN = System.getenv("HEROKU_API_TOKEN");

	private static final String HEROKU_CONFIGVARS_ENDPOINT;

	static {
		HEROKU_CONFIGVARS_ENDPOINT = String.format(
			"https://api.heroku.com/apps/%s/config-vars",
			System.getenv("HEROKU_APP_ID")
		);
	}

	private static final String HEROKU_WEBHOOK_SECRET = System.getenv("HEROKU_WEBHOOK_SECRET");

	@PostMapping
	ResponseEntity herokuWebhook(
		@RequestHeader("Heroku-Webhook-Hmac-SHA256") final String herokuWebhookHmacSHA256,
		@RequestBody final String requestBody
	) {
		final String calculatedHmac = Base64.encodeBase64String(
			new HmacUtils(
				"HmacSHA256",
				HEROKU_WEBHOOK_SECRET
			).hmac(requestBody)
		);

		if (!calculatedHmac.equals(herokuWebhookHmacSHA256)) {
			LineNotifyUtils.notify(
				String.format(
					"%n%n`Heroku-Webhook-Hmac-SHA256`%n%s%n%n`calculatedHmac`%n%s",
					herokuWebhookHmacSHA256,
					calculatedHmac
				)
			);

			return ResponseEntity.noContent().build();
		}

		HttpGet httpGet = new HttpGet(HEROKU_CONFIGVARS_ENDPOINT);
		httpGet.setHeader(
			"Authorization",
			String.format(
				"Bearer %s",
				HEROKU_API_TOKEN
			)
		);
		httpGet.setHeader(
			"Accept",
			"application/vnd.heroku+json; version=3"
		);
		CloseableHttpResponse httpResponse = HttpClientUtils.execute(httpGet);
		int statusCode = httpResponse.getStatusLine().getStatusCode();
		if (HttpStatus.SC_OK != statusCode) {
			LineNotifyUtils.notify(
				String.format(
					"%n取得 `Config Vars` 時響應狀態為 `%d`",
					statusCode
				)
			);
			return ResponseEntity.noContent().build();
		}
		HttpEntity httpEntity = httpResponse.getEntity();
		if (Objects.isNull(httpEntity)) {
			LineNotifyUtils.notify("\n取得 `Config Vars` 時響應主體為 `null`");
			return ResponseEntity.noContent().build();
		}
		InputStream inputStream;
		try {
			inputStream = httpEntity.getContent();

			LineNotifyUtils.notify(
				String.format(
					"%n%s",
					IOUtils.toString(
						inputStream,
						StandardCharsets.UTF_8
					)
				)
			);

			inputStream.close();
			httpResponse.close();
		} catch (IOException | UnsupportedOperationException ex) {
			LineNotifyUtils.notify(
				String.format(
					"%n解析 `Config Vars` 響應主體時發生異常例外%n%n",
					ex.getMessage()
				)
			);
			return ResponseEntity.noContent().build();
		}

		return ResponseEntity.noContent().build();
	}
```
