Skip to content

Commit

Permalink
Initial webhook autoconfiguration setup (#1)
Browse files Browse the repository at this point in the history
* add typed handler to use objects of stripe framework

* adds exception handling

* setup dependencies properly

* use properties set in gradle properties for plugin mgmt

* add more examples and fix test

* add workflows

* add jacoco test report

* rename workflows and jobs

* move sonar into subproject description

* add jacocoTestReport to sonar workflow

* define sonar on root project

* exclude sample project from analysis

* add first set of tests

* test handler support methods

* fix event mock

* add publishing task

* define publication in same project that should be published

* add execution results to stripe

* add json property annotations

* adds quickstart guide

* add combined onCondition function

* add stripe class conditonal for autoconfiguration

* simplify and add tests

* add readme and getting started guide

* previousAttributes optional

* use event istead of event type

* pass original event onReceive

* add conditional evaluation debug log

* add test for evaluation report

* add conditon test
  • Loading branch information
MarvinSchramm authored Mar 1, 2021
1 parent 8869454 commit 708882c
Show file tree
Hide file tree
Showing 24 changed files with 1,487 additions and 1 deletion.
38 changes: 38 additions & 0 deletions .github/workflows/hndrs-gradle-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: gradle

# Controls when the action will run.
on:
pull_request:
branches: [ main ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

jobs:
check:
runs-on: ubuntu-latest

steps:
- name: git checkout
uses: actions/checkout@v2
with:
fetch-depth: 0

- name: setup java
uses: actions/setup-java@v1
with:
java-version: '11'

- name: gradle cache
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: test
run: |
./gradlew check
44 changes: 44 additions & 0 deletions .github/workflows/hndrs-gradle-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: gradle

# Controls when the action will run.
on:
push:
tags:
- v*

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

jobs:
publish:
runs-on: ubuntu-latest

steps:
- name: git checkout
uses: actions/checkout@v2
with:
fetch-depth: 0

- name: setup java
uses: actions/setup-java@v1
with:
java-version: '11'

- name: setup build cache
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: publish
env:
SONATYPE_USER: ${{ secrets.SONATYPE_USER }}
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
run: |
./gradlew publish
51 changes: 51 additions & 0 deletions .github/workflows/hndrs-gradle-sonar.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: gradle

# Controls when the action will run.
on:
push:
branches:
- main
pull_request:
branches: [ main ]
types: [ opened, synchronize, reopened ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

jobs:
analyse:
runs-on: ubuntu-latest

steps:
- name: git checkout
uses: actions/checkout@v2
with:
fetch-depth: 0

- name: setup java
uses: actions/setup-java@v1
with:
java-version: '11'

- name: gradle cache
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: sonar cache
uses: actions/cache@v1
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar

- name: analyse
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: ./gradlew check jacocoTestReport sonarqube --info
29 changes: 29 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.DS_Store
.gradle
**/build/
!gradle/wrapper/gradle-wrapper.jar


### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
**/out/

### NetBeans ###
/nbproject/private/
/build/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/%
109 changes: 109 additions & 0 deletions GETTING_STARTED_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
### What You Will Build

You will build an application that receives a subscription update stripe event by using ```StripeEventHandler```.

### What You Need

- About 15 minutes
- A favorite text editor or IDE
- JDK 11 or later
- Gradle

### Starting with Spring Initializr

For all Spring applications, you should start with the [Spring Initializr](https://start.spring.io/). The Initializr
offers a fast way to pull in all the dependencies you need for an application and does a lot of the set up for you. This
example needs only the Spring Web dependency, Java 11 and Gradle

Add the following to your ```build.gradle.kts``` or ```build.gradle```

```kotlin
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
id("org.springframework.boot") version "2.4.2"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
kotlin("jvm") version "1.4.21"
kotlin("plugin.spring") version "1.4.21"
}

repositories {
mavenCentral()
}

dependencies {
// add the stripe spring boot starter to your gradle build file
implementation("io.hndrs:stripe-spring-boot-starter:1.0.0")

// add the stripe java library
implementation("com.stripe:stripe-java:20.37.0")


implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}

tasks.withType<Test> {
useJUnitPlatform()
}


```

### Configure the stripe webhook

To configure the stripe webhook we need to set its ```signingSecret``` and the ```webhook-path```. Add the following
properties to ```src/main/resources/application```

```properties
hndrs.stripe.signingSecret=whsc_**********
hndrs.stripe.webhook-path=/stripe-events
```

> The signing secret can be obtained on your [Stripe Dashboard](https://dashboard.stripe.com/test/webhooks) or with the [Stripe CLI](https://stripe.com/docs/stripe-cli/webhooks)
### Create a Stripe Event Handler

With any webhook-event-based application, you need to create a receiver that responds to published webhook events. The
following implementation shows how to do so:

```kotlin
@Component
open class ExampleReceiver : StripeEventReceiver<Subscription>(Subscription::class.java) {

override fun onCondition(event: Event): Boolean {
// check the event type
return event.type == "customer.subscription.updated"
}

companion object {
private val LOG = LoggerFactory.getLogger(ExampleReceiver::class.java)
}
}
```

### Send a Test Message

Use the [Stripe Cli](https://stripe.com/docs/stripe-cli/webhooks) to send a test event.

#### Listen for events

```shell
stripe listen
```

#### Trigger an event

```shell
stripe trigger customer.subscription.updated
```
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021 hndrs
Copyright (c) 2021 Marvin Schramm (https://github.com/MarvinSchramm)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
83 changes: 83 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
[![Current Version](https://img.shields.io/maven-central/v/io.hndrs/hndrs_stripe-spring-boot-starter?style=for-the-badge&logo=sonar)](https://search.maven.org/search?q=io.hndrs)
[![Coverage](https://img.shields.io/sonar/coverage/hndrs_stripe-spring-boot-starter?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge)](https://sonarcloud.io/dashboard?id=hndrs_stripe-spring-boot-starter)
[![Supported Java Version](https://img.shields.io/badge/Supported%20Java%20Version-11%2B-informational?style=for-the-badge)]()
[![License](https://img.shields.io/github/license/hndrs/stripe-spring-boot-starter?style=for-the-badge)]()
[![Sponsor](https://img.shields.io/static/v1?logo=GitHub&label=Sponsor&message=%E2%9D%A4&color=ff69b4&style=for-the-badge)](https://github.com/sponsors/marvinschramm)

# stripe-spring-boot-starter

Follow the [Getting Started Guide](GETTING_STARTED_GUIDE.md) or look at the [Sample](/sample) to help setting up
stripe-spring-boot-starter.

#### Dependency

```kotlin
implementation("io.hndrs:stripe-spring-boot-starter:1.0.0")

//skip this if you already have the stripe dependency in your project
implementation("com.stripe:stripe-java:<version>")
```

> Gradle
#### Configuration

```properties
hndrs.stripe.signing-secret=whsec_*******************
hndrs.stripe.webhook-path=/stripe-events
```

> application.properties
#### StripeEventReceiver

There are 3 conditional methods that can be used to narrow the execution condition (Note: there is an
internal ```class``` conditional that makes sure that the receiver only receives the defined generic type. You can
override any of the following methods (by default they return true)

- ```onCondition(event: Event)```
- *It is recommended to use this conditional to check at least the event type*
- ```onReceive(stripeObject: Subscription)```
- *It is recommended to use this when your condition **only** needs values from the ```stripeObject``` for your
business logic
- ```onCondition(previousAttributes: Map<String, Any?>?)```
- *It is recommended to use this conditional when your condition needs **only** values from
the ```previousAttributes```*
- ```onCondition(previousAttributes: Map<String, Any?>?, stripeObject: Subscription)```
- *It is recommended to use this conditional when your condition needs a combination of the ```previousAttributes```
and the received ```stripeObject```*

Implementing a ```StripeEventReceiver``` looks like the following:

```kotlin
@Component
open class ExampleReceiver : StripeEventReceiver<Subscription>(Subscription::class.java) {

override fun onCondition(event: Event): Boolean {
// conditional based stripe event
return event.type == "customer.subscription.updated"
}

override fun onCondition(stripeObject: Subscription): Boolean {
// conditional based stripe object
return true
}

override fun onCondition(previousAttributes: Map<String, Any>): Boolean {
// conditional based on previousAttributes
return true
}

override fun onCondition(previousAttributes: Map<String, Any>, stripeObject: Subscription): Boolean {
// conditional based previousAttributes and stripe object
return true
}

override fun onReceive(stripeObject: Subscription) {
// do something with the received object
}
}
```

> The ```StripeEventReceiver``` generic needs to be subclass of a [StripeObject](https://github.com/stripe/stripe-java/blob/master/src/main/java/com/stripe/model/StripeObject.java)
Loading

0 comments on commit 708882c

Please sign in to comment.